@xapp/chat-widget 1.86.0 → 1.87.1
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/components/ActionBar/ActionBar.d.ts +0 -0
- package/dist/components/ActionBar/ActionBarCta.d.ts +0 -0
- package/dist/components/ChatHeader/ChatHeader.d.ts +0 -0
- package/dist/components/ChatMessagePart/ChatMessagePart.d.ts +0 -0
- package/dist/components/ChatTextMessage/ChatTextMessage.d.ts +0 -0
- package/dist/components/ExportButton/ExportButton.d.ts +0 -0
- package/dist/components/ExportButton/index.d.ts +0 -0
- package/dist/components/SourceLinks/SourceLinks.d.ts +0 -0
- package/dist/components/SourceLinks/index.d.ts +0 -0
- package/dist/components/ToolCallDisplay/ToolCallDisplay.d.ts +0 -0
- package/dist/index.css +1 -1
- package/dist/index.es.js +2301 -280
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +2300 -279
- package/dist/index.js.map +1 -1
- package/dist/store/ChatAction.d.ts +0 -0
- package/dist/store/ChatState.d.ts +0 -0
- package/dist/store/actions/writeMessage.d.ts +0 -0
- package/dist/xapp/ChatServerMessage.d.ts +0 -0
- package/dist/xapp/MCPChat.d.ts +0 -0
- package/dist/xapp-chat-widget.css +1 -1
- package/dist/xapp-chat-widget.js +4 -6
- package/dist/xapp-chat-widget.js.map +1 -1
- package/package.json +8 -7
package/dist/index.js
CHANGED
|
@@ -14,7 +14,6 @@ var lib = {};
|
|
|
14
14
|
|
|
15
15
|
var ActionBar$1 = {};
|
|
16
16
|
|
|
17
|
-
/*! Copyright (c) 2021, XAPPmedia */
|
|
18
17
|
Object.defineProperty(ActionBar$1, "__esModule", { value: true });
|
|
19
18
|
ActionBar$1.isActionBarChatButton = isActionBarChatButton;
|
|
20
19
|
ActionBar$1.isActionBarFormButton = isActionBarFormButton;
|
|
@@ -305,12 +304,103 @@ var ActionBarButton = function (_a) {
|
|
|
305
304
|
return (require$$0.jsxs("button", { className: buttonClassName, onClick: handleClick, "aria-label": ariaLabel, "aria-pressed": isActive, tabIndex: tabIndex, children: [require$$0.jsx(ActionBarIcon, { type: button.type, icon: button.icon, iconUrl: button.iconUrl, className: "xapp-action-bar__icon" }), button.label && (require$$0.jsx("span", { className: "xapp-action-bar__label", children: button.label }))] }));
|
|
306
305
|
};
|
|
307
306
|
|
|
307
|
+
/**
|
|
308
|
+
* CTA banner for ActionBar - a full-width rectangular banner that appears
|
|
309
|
+
* above the action bar to prompt users to engage with chat.
|
|
310
|
+
*/
|
|
311
|
+
var ActionBarCta = function (_a) {
|
|
312
|
+
var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
313
|
+
var config = _a.config, isMobile = _a.isMobile, onClick = _a.onClick, onDismiss = _a.onDismiss, hasUserInteracted = _a.hasUserInteracted;
|
|
314
|
+
var _m = require$$1.useState(false), isVisible = _m[0], setIsVisible = _m[1];
|
|
315
|
+
var _o = require$$1.useState(false), isAnimating = _o[0], setIsAnimating = _o[1];
|
|
316
|
+
var _p = require$$1.useState(false), isDismissed = _p[0], setIsDismissed = _p[1];
|
|
317
|
+
// For mobile overrides: null means "explicitly disabled", undefined means "use desktop default"
|
|
318
|
+
var mobileMessage = isMobile ? (_b = config.mobile) === null || _b === void 0 ? void 0 : _b.message : undefined;
|
|
319
|
+
var message = mobileMessage !== undefined ? mobileMessage : config.message;
|
|
320
|
+
var delay = isMobile && ((_c = config.mobile) === null || _c === void 0 ? void 0 : _c.delay) !== undefined ? config.mobile.delay : ((_d = config.delay) !== null && _d !== void 0 ? _d : 0);
|
|
321
|
+
var mobileTimeout = isMobile ? (_e = config.mobile) === null || _e === void 0 ? void 0 : _e.timeout : undefined;
|
|
322
|
+
var timeout = mobileTimeout !== undefined ? mobileTimeout : config.timeout;
|
|
323
|
+
var mobileAnimation = isMobile ? (_f = config.mobile) === null || _f === void 0 ? void 0 : _f.animation : undefined;
|
|
324
|
+
var animation = mobileAnimation !== undefined ? mobileAnimation : config.animation;
|
|
325
|
+
var animationDelay = isMobile && ((_g = config.mobile) === null || _g === void 0 ? void 0 : _g.animationDelay) !== undefined
|
|
326
|
+
? config.mobile.animationDelay
|
|
327
|
+
: ((_h = config.animationDelay) !== null && _h !== void 0 ? _h : delay);
|
|
328
|
+
var animationTimeout = isMobile && ((_j = config.mobile) === null || _j === void 0 ? void 0 : _j.animationTimeout) !== undefined
|
|
329
|
+
? config.mobile.animationTimeout
|
|
330
|
+
: config.animationTimeout;
|
|
331
|
+
var dismissible = isMobile && ((_k = config.mobile) === null || _k === void 0 ? void 0 : _k.dismissible) !== undefined
|
|
332
|
+
? config.mobile.dismissible
|
|
333
|
+
: ((_l = config.dismissible) !== null && _l !== void 0 ? _l : true);
|
|
334
|
+
// Show CTA after delay
|
|
335
|
+
require$$1.useEffect(function () {
|
|
336
|
+
if (!message || hasUserInteracted || isDismissed) {
|
|
337
|
+
setIsVisible(false);
|
|
338
|
+
return undefined;
|
|
339
|
+
}
|
|
340
|
+
var showTimer = setTimeout(function () {
|
|
341
|
+
setIsVisible(true);
|
|
342
|
+
}, delay);
|
|
343
|
+
return function () { return clearTimeout(showTimer); };
|
|
344
|
+
}, [message, delay, hasUserInteracted, isDismissed]);
|
|
345
|
+
// Hide CTA after timeout
|
|
346
|
+
require$$1.useEffect(function () {
|
|
347
|
+
if (!isVisible || !timeout) {
|
|
348
|
+
return undefined;
|
|
349
|
+
}
|
|
350
|
+
var hideTimer = setTimeout(function () {
|
|
351
|
+
setIsVisible(false);
|
|
352
|
+
}, timeout);
|
|
353
|
+
return function () { return clearTimeout(hideTimer); };
|
|
354
|
+
}, [isVisible, timeout]);
|
|
355
|
+
// Start animation after animationDelay
|
|
356
|
+
require$$1.useEffect(function () {
|
|
357
|
+
if (!animation || hasUserInteracted || isDismissed) {
|
|
358
|
+
setIsAnimating(false);
|
|
359
|
+
return undefined;
|
|
360
|
+
}
|
|
361
|
+
var animateTimer = setTimeout(function () {
|
|
362
|
+
setIsAnimating(true);
|
|
363
|
+
}, animationDelay);
|
|
364
|
+
return function () { return clearTimeout(animateTimer); };
|
|
365
|
+
}, [animation, animationDelay, hasUserInteracted, isDismissed]);
|
|
366
|
+
// Stop animation after animationTimeout
|
|
367
|
+
require$$1.useEffect(function () {
|
|
368
|
+
if (!isAnimating || !animationTimeout) {
|
|
369
|
+
return undefined;
|
|
370
|
+
}
|
|
371
|
+
var stopTimer = setTimeout(function () {
|
|
372
|
+
setIsAnimating(false);
|
|
373
|
+
}, animationTimeout);
|
|
374
|
+
return function () { return clearTimeout(stopTimer); };
|
|
375
|
+
}, [isAnimating, animationTimeout]);
|
|
376
|
+
var handleClick = function () {
|
|
377
|
+
onClick();
|
|
378
|
+
setIsVisible(false);
|
|
379
|
+
};
|
|
380
|
+
var handleDismiss = function (e) {
|
|
381
|
+
e.stopPropagation();
|
|
382
|
+
setIsDismissed(true);
|
|
383
|
+
setIsVisible(false);
|
|
384
|
+
onDismiss();
|
|
385
|
+
};
|
|
386
|
+
if (!isVisible || !message) {
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
var animationClass = isAnimating && animation ? "xapp-action-bar-cta--".concat(animation) : "";
|
|
390
|
+
return (require$$0.jsxs("div", { className: "xapp-action-bar-cta ".concat(animationClass), onClick: handleClick, role: "button", tabIndex: 0, "aria-label": message, onKeyDown: function (e) {
|
|
391
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
392
|
+
e.preventDefault();
|
|
393
|
+
handleClick();
|
|
394
|
+
}
|
|
395
|
+
}, children: [require$$0.jsx("span", { className: "xapp-action-bar-cta__message", children: message }), dismissible && (require$$0.jsx("button", { className: "xapp-action-bar-cta__dismiss", onClick: handleDismiss, "aria-label": "Dismiss", type: "button", children: require$$0.jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: require$$0.jsx("path", { d: "M13 1L1 13M1 1L13 13", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }))] }));
|
|
396
|
+
};
|
|
397
|
+
|
|
308
398
|
var DEFAULT_MOBILE_BREAKPOINT = 768;
|
|
309
399
|
var RESIZE_DEBOUNCE_MS = 150;
|
|
310
400
|
var ActionBar = function (_a) {
|
|
311
|
-
var _b, _c;
|
|
312
|
-
var config = _a.config, visible = _a.visible, chatDisabled = _a.chatDisabled, onChatClick = _a.onChatClick, onChatMinimize = _a.onChatMinimize, onFormClick = _a.onFormClick;
|
|
313
|
-
var
|
|
401
|
+
var _b, _c, _d;
|
|
402
|
+
var config = _a.config, visible = _a.visible, chatDisabled = _a.chatDisabled, _e = _a.hasUserInteracted, hasUserInteracted = _e === void 0 ? false : _e, onChatClick = _a.onChatClick, onChatMinimize = _a.onChatMinimize, onFormClick = _a.onFormClick, onCtaDismiss = _a.onCtaDismiss;
|
|
403
|
+
var _f = require$$1.useState(false), isMobile = _f[0], setIsMobile = _f[1];
|
|
314
404
|
var mobileBreakpoint = (_b = config.mobileBreakpoint) !== null && _b !== void 0 ? _b : DEFAULT_MOBILE_BREAKPOINT;
|
|
315
405
|
var position = (_c = config.position) !== null && _c !== void 0 ? _c : "right";
|
|
316
406
|
var resizeTimeoutRef = require$$1.useRef(null);
|
|
@@ -346,7 +436,19 @@ var ActionBar = function (_a) {
|
|
|
346
436
|
]
|
|
347
437
|
.filter(Boolean)
|
|
348
438
|
.join(" ");
|
|
349
|
-
|
|
439
|
+
// Determine if CTA should be shown:
|
|
440
|
+
// - Must have CTA config with a message
|
|
441
|
+
// - Chat must not be visible (user hasn't opened it)
|
|
442
|
+
// - Chat must not be disabled
|
|
443
|
+
var hasChatButton = config.buttons.some(lib.isActionBarChatButton);
|
|
444
|
+
var showCta = ((_d = config.cta) === null || _d === void 0 ? void 0 : _d.message) && !visible && !chatDisabled && hasChatButton;
|
|
445
|
+
var handleCtaClick = function () {
|
|
446
|
+
onChatClick();
|
|
447
|
+
};
|
|
448
|
+
var handleCtaDismiss = function () {
|
|
449
|
+
onCtaDismiss === null || onCtaDismiss === void 0 ? void 0 : onCtaDismiss();
|
|
450
|
+
};
|
|
451
|
+
return (require$$0.jsxs("div", { className: classNames, role: "toolbar", "aria-label": "Chat actions", children: [showCta && (require$$0.jsx("div", { className: "xapp-action-bar-cta-wrapper", children: require$$0.jsx(ActionBarCta, { config: config.cta, isMobile: isMobile, onClick: handleCtaClick, onDismiss: handleCtaDismiss, hasUserInteracted: hasUserInteracted }) })), require$$0.jsx("div", { className: "xapp-action-bar__buttons", children: filteredButtons.map(function (button) { return (require$$0.jsx(ActionBarButton, { button: button, chatActive: visible, onChatClick: onChatClick, onChatMinimize: onChatMinimize, onFormClick: onFormClick, isMobile: isMobile }, button.id)); }) })] }));
|
|
350
452
|
};
|
|
351
453
|
|
|
352
454
|
/**
|
|
@@ -979,14 +1081,16 @@ function getServerConfig(env) {
|
|
|
979
1081
|
}
|
|
980
1082
|
function useServerConfig(env, nonce) {
|
|
981
1083
|
var connection = getServerConfig(env);
|
|
982
|
-
var accountKey = connection.accountKey, serverUrl = connection.serverUrl, type = connection.type, timeout = connection.timeout;
|
|
1084
|
+
var accountKey = connection.accountKey, serverUrl = connection.serverUrl, type = connection.type, timeout = connection.timeout, backgroundSleepDelay = connection.backgroundSleepDelay, mcp = connection.mcp;
|
|
983
1085
|
return require$$1.useMemo(function () { return ({
|
|
984
1086
|
accountKey: accountKey,
|
|
985
1087
|
serverUrl: serverUrl,
|
|
986
1088
|
timeout: timeout,
|
|
987
1089
|
type: type,
|
|
988
1090
|
nonce: nonce,
|
|
989
|
-
|
|
1091
|
+
backgroundSleepDelay: backgroundSleepDelay,
|
|
1092
|
+
mcp: mcp,
|
|
1093
|
+
}); }, [accountKey, serverUrl, timeout, type, nonce, backgroundSleepDelay, mcp]);
|
|
990
1094
|
}
|
|
991
1095
|
function useConnectionInfo(env) {
|
|
992
1096
|
var nonce = reactRedux.useSelector(function (state) { return state.connection.nonce; });
|
|
@@ -995,7 +1099,7 @@ function useConnectionInfo(env) {
|
|
|
995
1099
|
}
|
|
996
1100
|
|
|
997
1101
|
var Avatar = function (props) {
|
|
998
|
-
var _a, _b;
|
|
1102
|
+
var _a, _b, _c, _d, _e;
|
|
999
1103
|
var style = {};
|
|
1000
1104
|
var child;
|
|
1001
1105
|
var entity = props.entity;
|
|
@@ -1018,7 +1122,9 @@ var Avatar = function (props) {
|
|
|
1018
1122
|
child = getVisitorSvg();
|
|
1019
1123
|
}
|
|
1020
1124
|
var hasImage = !!style.backgroundImage || !!child;
|
|
1021
|
-
|
|
1125
|
+
// Show AI badge if configured and the entity is an AI
|
|
1126
|
+
var showAiBadge = ((_e = (_d = (_c = chatConfig === null || chatConfig === void 0 ? void 0 : chatConfig.env) === null || _c === void 0 ? void 0 : _c.connection) === null || _d === void 0 ? void 0 : _d.mcp) === null || _e === void 0 ? void 0 : _e.showAiBadge) && (entity === null || entity === void 0 ? void 0 : entity.isAI);
|
|
1127
|
+
return (require$$0.jsxs("div", { className: "avatar ".concat(agentAva ? "avatar--agent" : "avatar--visitor", " ").concat(!hasImage ? "avatar--empty" : ""), style: style, title: entity ? entity.display_name : "Agent", children: [child, showAiBadge && require$$0.jsx("span", { className: "avatar__ai-badge", children: "AI" })] }));
|
|
1022
1128
|
};
|
|
1023
1129
|
/**
|
|
1024
1130
|
* Generates an SVG based on the
|
|
@@ -1678,14 +1784,14 @@ function useOpenUrlCallback() {
|
|
|
1678
1784
|
}, [env]);
|
|
1679
1785
|
}
|
|
1680
1786
|
|
|
1681
|
-
function writeMessage(msg, user) {
|
|
1787
|
+
function writeMessage(msg, user, timestamp) {
|
|
1682
1788
|
return {
|
|
1683
1789
|
type: "synthetic",
|
|
1684
1790
|
detail: {
|
|
1685
1791
|
type: "write_msg",
|
|
1686
1792
|
user: user,
|
|
1687
1793
|
msg: msg,
|
|
1688
|
-
timestamp: +new Date()
|
|
1794
|
+
timestamp: timestamp !== undefined ? timestamp : +new Date()
|
|
1689
1795
|
}
|
|
1690
1796
|
};
|
|
1691
1797
|
}
|
|
@@ -1697,16 +1803,19 @@ function executeAction(text, meta) {
|
|
|
1697
1803
|
attributes = __assign(__assign({}, attributes), { currentUrl: window.location.href });
|
|
1698
1804
|
var _a = meta || {}, token = _a.token, rest = __rest(_a, ["token"]);
|
|
1699
1805
|
attributes = __assign(__assign({}, attributes), rest);
|
|
1806
|
+
// Add user message to Redux BEFORE sending to server
|
|
1807
|
+
// to ensure it appears above the bot's streaming response
|
|
1808
|
+
var visitor = getState().visitor;
|
|
1809
|
+
var userMsgTimestamp = +new Date();
|
|
1810
|
+
dispatch(writeMessage({
|
|
1811
|
+
text: !token ? text : "".concat(text, " (from the list)"),
|
|
1812
|
+
}, visitor, userMsgTimestamp));
|
|
1700
1813
|
chatServer.sendChatMsg({ text: text, token: token, attributes: attributes }, function (err) {
|
|
1701
1814
|
if (err) {
|
|
1702
1815
|
log("Error sending message", err);
|
|
1703
1816
|
return;
|
|
1704
1817
|
}
|
|
1705
1818
|
});
|
|
1706
|
-
var visitor = getState().visitor;
|
|
1707
|
-
dispatch(writeMessage({
|
|
1708
|
-
text: !token ? text : "".concat(text, " (from the list)"),
|
|
1709
|
-
}, visitor));
|
|
1710
1819
|
}; };
|
|
1711
1820
|
}
|
|
1712
1821
|
|
|
@@ -2603,236 +2712,231 @@ var parseSuggestionsResponse_1 = parseSuggestionsResponse;
|
|
|
2603
2712
|
var useSuggestionsFetch_1 = useSuggestionsFetch$1;
|
|
2604
2713
|
var uuid_1 = uuid;
|
|
2605
2714
|
|
|
2606
|
-
|
|
2607
|
-
* Sends a POST to your STENTOR based server.
|
|
2608
|
-
*
|
|
2609
|
-
* @param data
|
|
2610
|
-
* @param url
|
|
2611
|
-
* @param key
|
|
2612
|
-
* @param signal
|
|
2613
|
-
* @returns
|
|
2614
|
-
*/
|
|
2615
|
-
function postMessageToStentor(data, url, key, signal) {
|
|
2616
|
-
return __awaiter$1(this, void 0, void 0, function () {
|
|
2617
|
-
var body, response;
|
|
2618
|
-
return __generator$1(this, function (_a) {
|
|
2619
|
-
switch (_a.label) {
|
|
2620
|
-
case 0:
|
|
2621
|
-
body = JSON.stringify(data);
|
|
2622
|
-
log("URL: ".concat(url));
|
|
2623
|
-
log("BODY: ".concat(body));
|
|
2624
|
-
return [4 /*yield*/, fetch(url, {
|
|
2625
|
-
method: "POST",
|
|
2626
|
-
headers: {
|
|
2627
|
-
"Content-Type": "application/json",
|
|
2628
|
-
"Authorization": "Bearer ".concat(key),
|
|
2629
|
-
},
|
|
2630
|
-
body: body,
|
|
2631
|
-
mode: "cors",
|
|
2632
|
-
signal: signal
|
|
2633
|
-
})];
|
|
2634
|
-
case 1:
|
|
2635
|
-
response = _a.sent();
|
|
2636
|
-
if (!response.ok) {
|
|
2637
|
-
throw new Error("Status ".concat(response.status, ", Text: ").concat(response.statusText));
|
|
2638
|
-
}
|
|
2639
|
-
return [2 /*return*/, response.json()];
|
|
2640
|
-
}
|
|
2641
|
-
});
|
|
2642
|
-
});
|
|
2643
|
-
}
|
|
2715
|
+
var cjs = {};
|
|
2644
2716
|
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
}
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
function
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
}
|
|
2763
|
-
else {
|
|
2764
|
-
endSession = !reprompt || !(reprompt.displayText || reprompt.ssml);
|
|
2765
|
-
responseMessage = {
|
|
2766
|
-
type: "msg",
|
|
2767
|
-
timestamp: now,
|
|
2768
|
-
msg: {
|
|
2769
|
-
displays: botResponse.displays,
|
|
2770
|
-
context: (_d = (_c = botResponse.context) === null || _c === void 0 ? void 0 : _c.active) === null || _d === void 0 ? void 0 : _d.map(function (ctx) { return ctx.name; })
|
|
2771
|
-
},
|
|
2772
|
-
endSession: endSession,
|
|
2773
|
-
user: undefined
|
|
2774
|
-
};
|
|
2775
|
-
if (text && !html) {
|
|
2776
|
-
responseMessage.msg = __assign(__assign({}, responseMessage.msg), { text: text });
|
|
2777
|
-
}
|
|
2778
|
-
if (html) {
|
|
2779
|
-
responseMessage.msg = __assign(__assign({}, responseMessage.msg), { html: html });
|
|
2780
|
-
}
|
|
2781
|
-
if ((_e = outputSpeech === null || outputSpeech === void 0 ? void 0 : outputSpeech.suggestions) === null || _e === void 0 ? void 0 : _e.length) {
|
|
2782
|
-
responseMessage.msg.options = outputSpeech.suggestions.map(function (suggestion) {
|
|
2783
|
-
if (typeof suggestion === "string") {
|
|
2784
|
-
// Simple chips (strings)
|
|
2785
|
-
return suggestion;
|
|
2786
|
-
}
|
|
2787
|
-
else {
|
|
2788
|
-
// "call out" chips
|
|
2789
|
-
return {
|
|
2790
|
-
label: suggestion.title,
|
|
2791
|
-
actionUrl: suggestion.url
|
|
2792
|
-
};
|
|
2793
|
-
}
|
|
2794
|
-
});
|
|
2795
|
-
}
|
|
2796
|
-
}
|
|
2797
|
-
return responseMessage;
|
|
2798
|
-
}
|
|
2799
|
-
function getPermissionRequestType(type) {
|
|
2800
|
-
switch (type) {
|
|
2801
|
-
case "PERMISSION_EMAIL":
|
|
2802
|
-
return "EMAIL";
|
|
2803
|
-
case "PERMISSION_LOCATION_PRECISE":
|
|
2804
|
-
return "LOCATION_PRECISE";
|
|
2805
|
-
default:
|
|
2806
|
-
throw new Error("Unsupported permission: ".concat(type));
|
|
2807
|
-
}
|
|
2808
|
-
}
|
|
2809
|
-
function getPermissionResponse(botResponse, now) {
|
|
2810
|
-
var _a;
|
|
2811
|
-
var type = getPermissionRequestType(botResponse.system);
|
|
2812
|
-
var outputSpeech = getOutput(botResponse.outputSpeech);
|
|
2813
|
-
var permissionPrimerAccepted = botResponse.data.permissionPrimerAccepted;
|
|
2814
|
-
var permissionDenied = botResponse.data.permissionDenied;
|
|
2815
|
-
return {
|
|
2816
|
-
type: "permissionRequest",
|
|
2817
|
-
timestamp: now,
|
|
2818
|
-
msg: {
|
|
2819
|
-
text: (_a = outputSpeech === null || outputSpeech === void 0 ? void 0 : outputSpeech.displayText) !== null && _a !== void 0 ? _a : "".concat(botResponse.data.permissionRequestTTSContext),
|
|
2820
|
-
permissionRequest: {
|
|
2821
|
-
time: now,
|
|
2822
|
-
type: type,
|
|
2823
|
-
approve: typeof permissionPrimerAccepted === "object" ? responseToMessage({
|
|
2824
|
-
outputSpeech: permissionPrimerAccepted
|
|
2825
|
-
}).msg : undefined,
|
|
2826
|
-
deny: typeof permissionDenied === "object" ? responseToMessage({
|
|
2827
|
-
outputSpeech: permissionDenied
|
|
2828
|
-
}).msg : undefined
|
|
2829
|
-
}
|
|
2830
|
-
},
|
|
2831
|
-
endSession: false,
|
|
2832
|
-
user: undefined //todo: set
|
|
2833
|
-
};
|
|
2717
|
+
var fetch$1 = {};
|
|
2718
|
+
|
|
2719
|
+
var parse$3 = {};
|
|
2720
|
+
|
|
2721
|
+
Object.defineProperty(parse$3, "__esModule", { value: true });
|
|
2722
|
+
parse$3.getMessages = parse$3.getLines = parse$3.getBytes = void 0;
|
|
2723
|
+
async function getBytes(stream, onChunk) {
|
|
2724
|
+
const reader = stream.getReader();
|
|
2725
|
+
let result;
|
|
2726
|
+
while (!(result = await reader.read()).done) {
|
|
2727
|
+
onChunk(result.value);
|
|
2728
|
+
}
|
|
2729
|
+
}
|
|
2730
|
+
parse$3.getBytes = getBytes;
|
|
2731
|
+
function getLines(onLine) {
|
|
2732
|
+
let buffer;
|
|
2733
|
+
let position;
|
|
2734
|
+
let fieldLength;
|
|
2735
|
+
let discardTrailingNewline = false;
|
|
2736
|
+
return function onChunk(arr) {
|
|
2737
|
+
if (buffer === undefined) {
|
|
2738
|
+
buffer = arr;
|
|
2739
|
+
position = 0;
|
|
2740
|
+
fieldLength = -1;
|
|
2741
|
+
}
|
|
2742
|
+
else {
|
|
2743
|
+
buffer = concat(buffer, arr);
|
|
2744
|
+
}
|
|
2745
|
+
const bufLength = buffer.length;
|
|
2746
|
+
let lineStart = 0;
|
|
2747
|
+
while (position < bufLength) {
|
|
2748
|
+
if (discardTrailingNewline) {
|
|
2749
|
+
if (buffer[position] === 10) {
|
|
2750
|
+
lineStart = ++position;
|
|
2751
|
+
}
|
|
2752
|
+
discardTrailingNewline = false;
|
|
2753
|
+
}
|
|
2754
|
+
let lineEnd = -1;
|
|
2755
|
+
for (; position < bufLength && lineEnd === -1; ++position) {
|
|
2756
|
+
switch (buffer[position]) {
|
|
2757
|
+
case 58:
|
|
2758
|
+
if (fieldLength === -1) {
|
|
2759
|
+
fieldLength = position - lineStart;
|
|
2760
|
+
}
|
|
2761
|
+
break;
|
|
2762
|
+
case 13:
|
|
2763
|
+
discardTrailingNewline = true;
|
|
2764
|
+
case 10:
|
|
2765
|
+
lineEnd = position;
|
|
2766
|
+
break;
|
|
2767
|
+
}
|
|
2768
|
+
}
|
|
2769
|
+
if (lineEnd === -1) {
|
|
2770
|
+
break;
|
|
2771
|
+
}
|
|
2772
|
+
onLine(buffer.subarray(lineStart, lineEnd), fieldLength);
|
|
2773
|
+
lineStart = position;
|
|
2774
|
+
fieldLength = -1;
|
|
2775
|
+
}
|
|
2776
|
+
if (lineStart === bufLength) {
|
|
2777
|
+
buffer = undefined;
|
|
2778
|
+
}
|
|
2779
|
+
else if (lineStart !== 0) {
|
|
2780
|
+
buffer = buffer.subarray(lineStart);
|
|
2781
|
+
position -= lineStart;
|
|
2782
|
+
}
|
|
2783
|
+
};
|
|
2784
|
+
}
|
|
2785
|
+
parse$3.getLines = getLines;
|
|
2786
|
+
function getMessages(onId, onRetry, onMessage) {
|
|
2787
|
+
let message = newMessage();
|
|
2788
|
+
const decoder = new TextDecoder();
|
|
2789
|
+
return function onLine(line, fieldLength) {
|
|
2790
|
+
if (line.length === 0) {
|
|
2791
|
+
onMessage === null || onMessage === void 0 ? void 0 : onMessage(message);
|
|
2792
|
+
message = newMessage();
|
|
2793
|
+
}
|
|
2794
|
+
else if (fieldLength > 0) {
|
|
2795
|
+
const field = decoder.decode(line.subarray(0, fieldLength));
|
|
2796
|
+
const valueOffset = fieldLength + (line[fieldLength + 1] === 32 ? 2 : 1);
|
|
2797
|
+
const value = decoder.decode(line.subarray(valueOffset));
|
|
2798
|
+
switch (field) {
|
|
2799
|
+
case 'data':
|
|
2800
|
+
message.data = message.data
|
|
2801
|
+
? message.data + '\n' + value
|
|
2802
|
+
: value;
|
|
2803
|
+
break;
|
|
2804
|
+
case 'event':
|
|
2805
|
+
message.event = value;
|
|
2806
|
+
break;
|
|
2807
|
+
case 'id':
|
|
2808
|
+
onId(message.id = value);
|
|
2809
|
+
break;
|
|
2810
|
+
case 'retry':
|
|
2811
|
+
const retry = parseInt(value, 10);
|
|
2812
|
+
if (!isNaN(retry)) {
|
|
2813
|
+
onRetry(message.retry = retry);
|
|
2814
|
+
}
|
|
2815
|
+
break;
|
|
2816
|
+
}
|
|
2817
|
+
}
|
|
2818
|
+
};
|
|
2819
|
+
}
|
|
2820
|
+
parse$3.getMessages = getMessages;
|
|
2821
|
+
function concat(a, b) {
|
|
2822
|
+
const res = new Uint8Array(a.length + b.length);
|
|
2823
|
+
res.set(a);
|
|
2824
|
+
res.set(b, a.length);
|
|
2825
|
+
return res;
|
|
2826
|
+
}
|
|
2827
|
+
function newMessage() {
|
|
2828
|
+
return {
|
|
2829
|
+
data: '',
|
|
2830
|
+
event: '',
|
|
2831
|
+
id: '',
|
|
2832
|
+
retry: undefined,
|
|
2833
|
+
};
|
|
2834
2834
|
}
|
|
2835
2835
|
|
|
2836
|
+
(function (exports$1) {
|
|
2837
|
+
var __rest = (commonjsGlobal && commonjsGlobal.__rest) || function (s, e) {
|
|
2838
|
+
var t = {};
|
|
2839
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
2840
|
+
t[p] = s[p];
|
|
2841
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
2842
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
2843
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
2844
|
+
t[p[i]] = s[p[i]];
|
|
2845
|
+
}
|
|
2846
|
+
return t;
|
|
2847
|
+
};
|
|
2848
|
+
Object.defineProperty(exports$1, "__esModule", { value: true });
|
|
2849
|
+
exports$1.fetchEventSource = exports$1.EventStreamContentType = void 0;
|
|
2850
|
+
const parse_1 = parse$3;
|
|
2851
|
+
exports$1.EventStreamContentType = 'text/event-stream';
|
|
2852
|
+
const DefaultRetryInterval = 1000;
|
|
2853
|
+
const LastEventId = 'last-event-id';
|
|
2854
|
+
function fetchEventSource(input, _a) {
|
|
2855
|
+
var { signal: inputSignal, headers: inputHeaders, onopen: inputOnOpen, onmessage, onclose, onerror, openWhenHidden, fetch: inputFetch } = _a, rest = __rest(_a, ["signal", "headers", "onopen", "onmessage", "onclose", "onerror", "openWhenHidden", "fetch"]);
|
|
2856
|
+
return new Promise((resolve, reject) => {
|
|
2857
|
+
const headers = Object.assign({}, inputHeaders);
|
|
2858
|
+
if (!headers.accept) {
|
|
2859
|
+
headers.accept = exports$1.EventStreamContentType;
|
|
2860
|
+
}
|
|
2861
|
+
let curRequestController;
|
|
2862
|
+
function onVisibilityChange() {
|
|
2863
|
+
curRequestController.abort();
|
|
2864
|
+
if (!document.hidden) {
|
|
2865
|
+
create();
|
|
2866
|
+
}
|
|
2867
|
+
}
|
|
2868
|
+
if (!openWhenHidden) {
|
|
2869
|
+
document.addEventListener('visibilitychange', onVisibilityChange);
|
|
2870
|
+
}
|
|
2871
|
+
let retryInterval = DefaultRetryInterval;
|
|
2872
|
+
let retryTimer = 0;
|
|
2873
|
+
function dispose() {
|
|
2874
|
+
document.removeEventListener('visibilitychange', onVisibilityChange);
|
|
2875
|
+
window.clearTimeout(retryTimer);
|
|
2876
|
+
curRequestController.abort();
|
|
2877
|
+
}
|
|
2878
|
+
inputSignal === null || inputSignal === void 0 ? void 0 : inputSignal.addEventListener('abort', () => {
|
|
2879
|
+
dispose();
|
|
2880
|
+
resolve();
|
|
2881
|
+
});
|
|
2882
|
+
const fetch = inputFetch !== null && inputFetch !== void 0 ? inputFetch : window.fetch;
|
|
2883
|
+
const onopen = inputOnOpen !== null && inputOnOpen !== void 0 ? inputOnOpen : defaultOnOpen;
|
|
2884
|
+
async function create() {
|
|
2885
|
+
var _a;
|
|
2886
|
+
curRequestController = new AbortController();
|
|
2887
|
+
try {
|
|
2888
|
+
const response = await fetch(input, Object.assign(Object.assign({}, rest), { headers, signal: curRequestController.signal }));
|
|
2889
|
+
await onopen(response);
|
|
2890
|
+
await parse_1.getBytes(response.body, parse_1.getLines(parse_1.getMessages(id => {
|
|
2891
|
+
if (id) {
|
|
2892
|
+
headers[LastEventId] = id;
|
|
2893
|
+
}
|
|
2894
|
+
else {
|
|
2895
|
+
delete headers[LastEventId];
|
|
2896
|
+
}
|
|
2897
|
+
}, retry => {
|
|
2898
|
+
retryInterval = retry;
|
|
2899
|
+
}, onmessage)));
|
|
2900
|
+
onclose === null || onclose === void 0 ? void 0 : onclose();
|
|
2901
|
+
dispose();
|
|
2902
|
+
resolve();
|
|
2903
|
+
}
|
|
2904
|
+
catch (err) {
|
|
2905
|
+
if (!curRequestController.signal.aborted) {
|
|
2906
|
+
try {
|
|
2907
|
+
const interval = (_a = onerror === null || onerror === void 0 ? void 0 : onerror(err)) !== null && _a !== void 0 ? _a : retryInterval;
|
|
2908
|
+
window.clearTimeout(retryTimer);
|
|
2909
|
+
retryTimer = window.setTimeout(create, interval);
|
|
2910
|
+
}
|
|
2911
|
+
catch (innerErr) {
|
|
2912
|
+
dispose();
|
|
2913
|
+
reject(innerErr);
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
2916
|
+
}
|
|
2917
|
+
}
|
|
2918
|
+
create();
|
|
2919
|
+
});
|
|
2920
|
+
}
|
|
2921
|
+
exports$1.fetchEventSource = fetchEventSource;
|
|
2922
|
+
function defaultOnOpen(response) {
|
|
2923
|
+
const contentType = response.headers.get('content-type');
|
|
2924
|
+
if (!(contentType === null || contentType === void 0 ? void 0 : contentType.startsWith(exports$1.EventStreamContentType))) {
|
|
2925
|
+
throw new Error(`Expected content-type to be ${exports$1.EventStreamContentType}, Actual: ${contentType}`);
|
|
2926
|
+
}
|
|
2927
|
+
}
|
|
2928
|
+
|
|
2929
|
+
} (fetch$1));
|
|
2930
|
+
|
|
2931
|
+
(function (exports$1) {
|
|
2932
|
+
Object.defineProperty(exports$1, "__esModule", { value: true });
|
|
2933
|
+
exports$1.EventStreamContentType = exports$1.fetchEventSource = void 0;
|
|
2934
|
+
var fetch_1 = fetch$1;
|
|
2935
|
+
Object.defineProperty(exports$1, "fetchEventSource", { enumerable: true, get: function () { return fetch_1.fetchEventSource; } });
|
|
2936
|
+
Object.defineProperty(exports$1, "EventStreamContentType", { enumerable: true, get: function () { return fetch_1.EventStreamContentType; } });
|
|
2937
|
+
|
|
2938
|
+
} (cjs));
|
|
2939
|
+
|
|
2836
2940
|
/**
|
|
2837
2941
|
* Default configuration messages
|
|
2838
2942
|
* @returns
|
|
@@ -2879,6 +2983,1533 @@ function setSessionId(sessionId) {
|
|
|
2879
2983
|
};
|
|
2880
2984
|
}
|
|
2881
2985
|
|
|
2986
|
+
// Endpoint path constants
|
|
2987
|
+
var HOMEOWNER_ENDPOINT_PATH = "/chat/stream";
|
|
2988
|
+
var BUSINESS_OWNER_ENDPOINT_PATH = "/api/chat/stream";
|
|
2989
|
+
// Default retry configuration for MCP connections
|
|
2990
|
+
var DEFAULT_MCP_RETRY_CONFIG = [
|
|
2991
|
+
{ retry: 0, delay: 0, text: "" }, // Initial attempt (silent)
|
|
2992
|
+
{ retry: 1, delay: 3, text: "Having trouble connecting. Retrying in ${sec} seconds..." },
|
|
2993
|
+
{ retry: 2, delay: 5, text: "Still having trouble. Retrying in ${sec} seconds..." },
|
|
2994
|
+
{ retry: 0, delay: 0, text: "Unable to connect to the assistant. Please try again later." }, // Final failure
|
|
2995
|
+
];
|
|
2996
|
+
// Streaming throttle configuration
|
|
2997
|
+
// Average reading speed is ~250 words/min (~20 chars/sec). We go slightly faster.
|
|
2998
|
+
var STREAM_CHARS_PER_TICK = 3; // Characters to display per tick
|
|
2999
|
+
var STREAM_TICK_INTERVAL_MS = 30; // Milliseconds between ticks (~100 chars/sec)
|
|
3000
|
+
/**
|
|
3001
|
+
* Storage keys for MCP session persistence.
|
|
3002
|
+
* Uses localStorage directly (not Redux) for MCP-specific data.
|
|
3003
|
+
*/
|
|
3004
|
+
var MCP_STORAGE_KEYS = {
|
|
3005
|
+
USER_ID: "xapp_mcp_userId",
|
|
3006
|
+
SESSION_ID: "xapp_mcp_sessionId",
|
|
3007
|
+
};
|
|
3008
|
+
/**
|
|
3009
|
+
* Gets the platform string, preferring the modern userAgentData API.
|
|
3010
|
+
* Falls back to the deprecated navigator.platform for older browsers.
|
|
3011
|
+
*/
|
|
3012
|
+
function getPlatform() {
|
|
3013
|
+
// Modern API (Chrome 90+, Edge 90+, Opera 76+)
|
|
3014
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3015
|
+
var userAgentData = navigator.userAgentData;
|
|
3016
|
+
if (userAgentData === null || userAgentData === void 0 ? void 0 : userAgentData.platform) {
|
|
3017
|
+
return userAgentData.platform;
|
|
3018
|
+
}
|
|
3019
|
+
// Fallback for older browsers (deprecated but widely supported)
|
|
3020
|
+
return navigator.platform || undefined;
|
|
3021
|
+
}
|
|
3022
|
+
/**
|
|
3023
|
+
* Collects non-invasive device/browser fingerprint data.
|
|
3024
|
+
* This helps the server identify returning users without requiring login.
|
|
3025
|
+
*
|
|
3026
|
+
* PRIVACY NOTE: Only basic, non-identifying browser characteristics are collected.
|
|
3027
|
+
* This data cannot be used to personally identify individual users.
|
|
3028
|
+
*/
|
|
3029
|
+
function getFingerprint() {
|
|
3030
|
+
if (typeof window === "undefined") {
|
|
3031
|
+
return {};
|
|
3032
|
+
}
|
|
3033
|
+
return {
|
|
3034
|
+
userAgent: navigator.userAgent,
|
|
3035
|
+
language: navigator.language,
|
|
3036
|
+
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
3037
|
+
screenResolution: "".concat(window.screen.width, "x").concat(window.screen.height),
|
|
3038
|
+
platform: getPlatform(),
|
|
3039
|
+
colorDepth: window.screen.colorDepth,
|
|
3040
|
+
touchSupport: "ontouchstart" in window || navigator.maxTouchPoints > 0,
|
|
3041
|
+
};
|
|
3042
|
+
}
|
|
3043
|
+
/**
|
|
3044
|
+
* Retrieves stored MCP identity from localStorage.
|
|
3045
|
+
*/
|
|
3046
|
+
function getStoredIdentity() {
|
|
3047
|
+
if (typeof window === "undefined" || !window.localStorage) {
|
|
3048
|
+
return {};
|
|
3049
|
+
}
|
|
3050
|
+
try {
|
|
3051
|
+
return {
|
|
3052
|
+
userId: localStorage.getItem(MCP_STORAGE_KEYS.USER_ID) || undefined,
|
|
3053
|
+
sessionId: localStorage.getItem(MCP_STORAGE_KEYS.SESSION_ID) || undefined,
|
|
3054
|
+
};
|
|
3055
|
+
}
|
|
3056
|
+
catch (_a) {
|
|
3057
|
+
// localStorage may be unavailable (private browsing, etc.)
|
|
3058
|
+
return {};
|
|
3059
|
+
}
|
|
3060
|
+
}
|
|
3061
|
+
/**
|
|
3062
|
+
* Stores MCP identity to localStorage.
|
|
3063
|
+
*/
|
|
3064
|
+
function storeIdentity(userId, sessionId) {
|
|
3065
|
+
if (typeof window === "undefined" || !window.localStorage) {
|
|
3066
|
+
return;
|
|
3067
|
+
}
|
|
3068
|
+
try {
|
|
3069
|
+
if (userId) {
|
|
3070
|
+
localStorage.setItem(MCP_STORAGE_KEYS.USER_ID, userId);
|
|
3071
|
+
}
|
|
3072
|
+
if (sessionId) {
|
|
3073
|
+
localStorage.setItem(MCP_STORAGE_KEYS.SESSION_ID, sessionId);
|
|
3074
|
+
}
|
|
3075
|
+
}
|
|
3076
|
+
catch (_a) {
|
|
3077
|
+
// localStorage may be unavailable
|
|
3078
|
+
}
|
|
3079
|
+
}
|
|
3080
|
+
/**
|
|
3081
|
+
* Clears stored MCP session (but preserves userId for returning user identification).
|
|
3082
|
+
* Optionally clears the stored lastSeq for the given sessionId.
|
|
3083
|
+
*/
|
|
3084
|
+
function clearStoredSession(sessionId) {
|
|
3085
|
+
if (typeof window === "undefined" || !window.localStorage) {
|
|
3086
|
+
return;
|
|
3087
|
+
}
|
|
3088
|
+
try {
|
|
3089
|
+
localStorage.removeItem(MCP_STORAGE_KEYS.SESSION_ID);
|
|
3090
|
+
if (sessionId) {
|
|
3091
|
+
clearStoredLastSeq(sessionId);
|
|
3092
|
+
}
|
|
3093
|
+
}
|
|
3094
|
+
catch (_a) {
|
|
3095
|
+
// localStorage may be unavailable
|
|
3096
|
+
}
|
|
3097
|
+
}
|
|
3098
|
+
/**
|
|
3099
|
+
* Retrieves stored lastSeq from sessionStorage for replay on reconnect.
|
|
3100
|
+
* Uses sessionStorage (not localStorage) because seq is ephemeral to the browser tab.
|
|
3101
|
+
*/
|
|
3102
|
+
function getStoredLastSeq(sessionId) {
|
|
3103
|
+
if (typeof window === "undefined" || !window.sessionStorage) {
|
|
3104
|
+
return undefined;
|
|
3105
|
+
}
|
|
3106
|
+
try {
|
|
3107
|
+
var stored = sessionStorage.getItem("xapp_mcp_lastSeq_".concat(sessionId));
|
|
3108
|
+
return stored ? parseInt(stored, 10) : undefined;
|
|
3109
|
+
}
|
|
3110
|
+
catch (_a) {
|
|
3111
|
+
return undefined;
|
|
3112
|
+
}
|
|
3113
|
+
}
|
|
3114
|
+
/**
|
|
3115
|
+
* Persists lastSeq to sessionStorage for replay on reconnect.
|
|
3116
|
+
*/
|
|
3117
|
+
function storeLastSeq(sessionId, seq) {
|
|
3118
|
+
if (typeof window === "undefined" || !window.sessionStorage) {
|
|
3119
|
+
return;
|
|
3120
|
+
}
|
|
3121
|
+
try {
|
|
3122
|
+
sessionStorage.setItem("xapp_mcp_lastSeq_".concat(sessionId), String(seq));
|
|
3123
|
+
}
|
|
3124
|
+
catch (_a) {
|
|
3125
|
+
// sessionStorage may be unavailable
|
|
3126
|
+
}
|
|
3127
|
+
}
|
|
3128
|
+
/**
|
|
3129
|
+
* Clears stored lastSeq from sessionStorage.
|
|
3130
|
+
*/
|
|
3131
|
+
function clearStoredLastSeq(sessionId) {
|
|
3132
|
+
if (typeof window === "undefined" || !window.sessionStorage) {
|
|
3133
|
+
return;
|
|
3134
|
+
}
|
|
3135
|
+
try {
|
|
3136
|
+
sessionStorage.removeItem("xapp_mcp_lastSeq_".concat(sessionId));
|
|
3137
|
+
}
|
|
3138
|
+
catch (_a) {
|
|
3139
|
+
// sessionStorage may be unavailable
|
|
3140
|
+
}
|
|
3141
|
+
}
|
|
3142
|
+
/**
|
|
3143
|
+
* Gets the current page context from browser globals.
|
|
3144
|
+
* If an override is provided, uses that instead (useful for testing).
|
|
3145
|
+
*
|
|
3146
|
+
* PRIVACY NOTE: Query strings are stripped from URLs by default as they
|
|
3147
|
+
* may contain sensitive data (tokens, PII). Only origin + pathname are sent.
|
|
3148
|
+
*/
|
|
3149
|
+
function getPageContext(override) {
|
|
3150
|
+
var localTime = new Date().toISOString();
|
|
3151
|
+
// If override provided, merge with defaults (override wins)
|
|
3152
|
+
// Overrides are typically for testing and may include full URLs
|
|
3153
|
+
if (override && Object.keys(override).length > 0) {
|
|
3154
|
+
log("[getPageContext] Using override: ".concat(JSON.stringify(override)));
|
|
3155
|
+
return {
|
|
3156
|
+
url: override.url,
|
|
3157
|
+
path: override.path,
|
|
3158
|
+
title: override.title,
|
|
3159
|
+
referrer: override.referrer,
|
|
3160
|
+
localTime: localTime,
|
|
3161
|
+
};
|
|
3162
|
+
}
|
|
3163
|
+
if (typeof window === "undefined") {
|
|
3164
|
+
return { localTime: localTime };
|
|
3165
|
+
}
|
|
3166
|
+
// Strip query strings from URL for privacy (may contain tokens/PII)
|
|
3167
|
+
var urlWithoutQuery = window.location.origin + window.location.pathname;
|
|
3168
|
+
// Strip query strings from referrer as well
|
|
3169
|
+
var referrerWithoutQuery;
|
|
3170
|
+
if (document.referrer) {
|
|
3171
|
+
try {
|
|
3172
|
+
var refUrl = new URL(document.referrer);
|
|
3173
|
+
referrerWithoutQuery = refUrl.origin + refUrl.pathname;
|
|
3174
|
+
}
|
|
3175
|
+
catch (_a) {
|
|
3176
|
+
// Invalid referrer URL, skip it
|
|
3177
|
+
referrerWithoutQuery = undefined;
|
|
3178
|
+
}
|
|
3179
|
+
}
|
|
3180
|
+
return {
|
|
3181
|
+
url: urlWithoutQuery,
|
|
3182
|
+
path: window.location.pathname,
|
|
3183
|
+
title: document.title || undefined,
|
|
3184
|
+
referrer: referrerWithoutQuery,
|
|
3185
|
+
localTime: localTime,
|
|
3186
|
+
};
|
|
3187
|
+
}
|
|
3188
|
+
var MCPChat = /** @class */ (function () {
|
|
3189
|
+
function MCPChat(config, options) {
|
|
3190
|
+
this._userId = "";
|
|
3191
|
+
this._sessionId = "";
|
|
3192
|
+
this._accessToken = "";
|
|
3193
|
+
this._attributes = {};
|
|
3194
|
+
// Streaming throttle state
|
|
3195
|
+
this.streamBuffer = "";
|
|
3196
|
+
// New session initialization state
|
|
3197
|
+
this.isInitializingSession = false;
|
|
3198
|
+
// Session validation state (silent handshake - no stream dispatch)
|
|
3199
|
+
this.isValidatingSession = false;
|
|
3200
|
+
// Tool call tracking for debug mode
|
|
3201
|
+
this.currentToolCalls = [];
|
|
3202
|
+
// TODO: isReplaying and lastSeq are tracked for future replay support.
|
|
3203
|
+
// Wire up replay requests in wakeup() once the server endpoint supports it.
|
|
3204
|
+
this.isReplaying = false;
|
|
3205
|
+
// True when userId was assigned by the server (via connected SSE event),
|
|
3206
|
+
// meaning it should not be overwritten by the default client fingerprint.
|
|
3207
|
+
this.userIdIsServerAssigned = false;
|
|
3208
|
+
this.config = config;
|
|
3209
|
+
this.options = options;
|
|
3210
|
+
this.configurableMessages = options === null || options === void 0 ? void 0 : options.configurableMessages;
|
|
3211
|
+
this.isNewSession = false;
|
|
3212
|
+
this.hooks = options === null || options === void 0 ? void 0 : options.hooks;
|
|
3213
|
+
// Validate business_owner mode requires authToken
|
|
3214
|
+
var mode = config.mode || "homeowner";
|
|
3215
|
+
if (mode === "business_owner" && !config.authToken) {
|
|
3216
|
+
throw new Error("authToken is required for business_owner mode");
|
|
3217
|
+
}
|
|
3218
|
+
// Load stored identity from localStorage
|
|
3219
|
+
var storedIdentity = getStoredIdentity();
|
|
3220
|
+
if (storedIdentity.userId) {
|
|
3221
|
+
this._userId = storedIdentity.userId;
|
|
3222
|
+
this.userIdIsServerAssigned = true;
|
|
3223
|
+
log("[MCPChat] Loaded stored userId: ".concat(this._userId));
|
|
3224
|
+
}
|
|
3225
|
+
if (storedIdentity.sessionId) {
|
|
3226
|
+
this._sessionId = storedIdentity.sessionId;
|
|
3227
|
+
log("[MCPChat] Loaded stored sessionId: ".concat(this._sessionId));
|
|
3228
|
+
}
|
|
3229
|
+
// Config-provided sessionId takes precedence
|
|
3230
|
+
if (config.sessionId) {
|
|
3231
|
+
this._sessionId = config.sessionId;
|
|
3232
|
+
}
|
|
3233
|
+
// Load lastSeq from sessionStorage for replay on reconnect
|
|
3234
|
+
if (this._sessionId) {
|
|
3235
|
+
this.lastSeq = getStoredLastSeq(this._sessionId);
|
|
3236
|
+
if (this.lastSeq !== undefined) {
|
|
3237
|
+
log("[MCPChat] Loaded stored lastSeq: ".concat(this.lastSeq));
|
|
3238
|
+
}
|
|
3239
|
+
}
|
|
3240
|
+
}
|
|
3241
|
+
MCPChat.prototype.init = function (dispatch) {
|
|
3242
|
+
this.dispatch = dispatch;
|
|
3243
|
+
var mode = this.config.mode || "homeowner";
|
|
3244
|
+
log("MCPChat initialized with mode: ".concat(mode));
|
|
3245
|
+
// Warn if not using HTTPS with authentication
|
|
3246
|
+
if (mode === "business_owner" && !this.config.serverUrl.startsWith("https://")) {
|
|
3247
|
+
err("WARNING: Using non-HTTPS URL with authentication tokens. This is insecure in production.");
|
|
3248
|
+
}
|
|
3249
|
+
this.setConnectionStatus("online");
|
|
3250
|
+
this.setAccountStatus("online");
|
|
3251
|
+
// Dispatch bot joined early so it appears before the greeting.
|
|
3252
|
+
// getBot() is config-derived (uses options.bot), so it's safe to call before setVisitorInfo.
|
|
3253
|
+
this.userJoined(this.getBot(undefined));
|
|
3254
|
+
// Returning user with existing session - validate session silently
|
|
3255
|
+
// New user without session - initialize new session to get greeting
|
|
3256
|
+
if (this._sessionId) {
|
|
3257
|
+
this.validateSession();
|
|
3258
|
+
}
|
|
3259
|
+
else {
|
|
3260
|
+
this.initializeNewSession();
|
|
3261
|
+
}
|
|
3262
|
+
};
|
|
3263
|
+
/**
|
|
3264
|
+
* Validates an existing session with the server ("Silent Handshake").
|
|
3265
|
+
* Called when widget reopens with a stored sessionId.
|
|
3266
|
+
*
|
|
3267
|
+
* The server will respond with:
|
|
3268
|
+
* - "connected" event if session is valid
|
|
3269
|
+
* - "session_expired" event if session is gone (triggers initializeNewSession)
|
|
3270
|
+
*
|
|
3271
|
+
* This is lightweight - no LLM call, just a quick DB check to:
|
|
3272
|
+
* - Validate session before user tries to send a message
|
|
3273
|
+
* - Update lastActivityAt in persistent session
|
|
3274
|
+
* - Confirm userId is still valid
|
|
3275
|
+
*/
|
|
3276
|
+
MCPChat.prototype.validateSession = function () {
|
|
3277
|
+
return __awaiter$1(this, void 0, void 0, function () {
|
|
3278
|
+
var endpoint, error_1;
|
|
3279
|
+
return __generator$1(this, function (_a) {
|
|
3280
|
+
switch (_a.label) {
|
|
3281
|
+
case 0:
|
|
3282
|
+
endpoint = this.getEndpoint();
|
|
3283
|
+
log("Validating existing session at ".concat(endpoint, ", sessionId: ").concat(this._sessionId));
|
|
3284
|
+
this.isValidatingSession = true;
|
|
3285
|
+
_a.label = 1;
|
|
3286
|
+
case 1:
|
|
3287
|
+
_a.trys.push([1, 3, 4, 5]);
|
|
3288
|
+
return [4 /*yield*/, this.performSSERequest(this.buildRequestPayload({ resumeSession: true }))];
|
|
3289
|
+
case 2:
|
|
3290
|
+
_a.sent();
|
|
3291
|
+
log("Session validation complete");
|
|
3292
|
+
return [3 /*break*/, 5];
|
|
3293
|
+
case 3:
|
|
3294
|
+
error_1 = _a.sent();
|
|
3295
|
+
err("Failed to validate session: ".concat(error_1));
|
|
3296
|
+
// If validation fails (network error, etc.), clear session and start fresh
|
|
3297
|
+
clearStoredSession(this._sessionId);
|
|
3298
|
+
this._sessionId = "";
|
|
3299
|
+
this.lastSeq = undefined;
|
|
3300
|
+
this.initializeNewSession();
|
|
3301
|
+
return [3 /*break*/, 5];
|
|
3302
|
+
case 4:
|
|
3303
|
+
this.isValidatingSession = false;
|
|
3304
|
+
return [7 /*endfinally*/];
|
|
3305
|
+
case 5: return [2 /*return*/];
|
|
3306
|
+
}
|
|
3307
|
+
});
|
|
3308
|
+
});
|
|
3309
|
+
};
|
|
3310
|
+
/**
|
|
3311
|
+
* Builds the common request payload with identity and context.
|
|
3312
|
+
*/
|
|
3313
|
+
MCPChat.prototype.buildRequestPayload = function (additionalData) {
|
|
3314
|
+
if (additionalData === void 0) { additionalData = {}; }
|
|
3315
|
+
return __assign({
|
|
3316
|
+
// Identity - server generates these, we just send back what we have
|
|
3317
|
+
userId: this._userId || undefined, sessionId: this._sessionId || undefined,
|
|
3318
|
+
// Fingerprint for user identification (can be disabled for privacy)
|
|
3319
|
+
fingerprint: this.config.disableFingerprinting ? undefined : getFingerprint(),
|
|
3320
|
+
// Page context
|
|
3321
|
+
pageContext: getPageContext(this.config.pageContextOverride),
|
|
3322
|
+
// Client timestamp for latency measurement and clock drift detection
|
|
3323
|
+
clientTime: new Date().toISOString() }, additionalData);
|
|
3324
|
+
};
|
|
3325
|
+
/**
|
|
3326
|
+
* Initializes a new session by calling the endpoint with { newSession: true }.
|
|
3327
|
+
* This returns a greeting message and session ID without making an LLM call.
|
|
3328
|
+
*/
|
|
3329
|
+
MCPChat.prototype.initializeNewSession = function () {
|
|
3330
|
+
return __awaiter$1(this, void 0, void 0, function () {
|
|
3331
|
+
var endpoint, error_2;
|
|
3332
|
+
var _this = this;
|
|
3333
|
+
return __generator$1(this, function (_a) {
|
|
3334
|
+
switch (_a.label) {
|
|
3335
|
+
case 0:
|
|
3336
|
+
// Prevent multiple concurrent initialization requests
|
|
3337
|
+
if (this.isInitializingSession) {
|
|
3338
|
+
log("New session initialization already in progress, skipping");
|
|
3339
|
+
return [2 /*return*/];
|
|
3340
|
+
}
|
|
3341
|
+
endpoint = this.getEndpoint();
|
|
3342
|
+
log("Initializing new session at ".concat(endpoint));
|
|
3343
|
+
this.isInitializingSession = true;
|
|
3344
|
+
// Create a promise that resolves when initialization completes
|
|
3345
|
+
this.sessionInitPromise = new Promise(function (resolve) {
|
|
3346
|
+
_this.resolveSessionInit = resolve;
|
|
3347
|
+
});
|
|
3348
|
+
this.typing();
|
|
3349
|
+
_a.label = 1;
|
|
3350
|
+
case 1:
|
|
3351
|
+
_a.trys.push([1, 3, 4, 5]);
|
|
3352
|
+
return [4 /*yield*/, this.performSSERequest(this.buildRequestPayload({ newSession: true, generateGreeting: true }))];
|
|
3353
|
+
case 2:
|
|
3354
|
+
_a.sent();
|
|
3355
|
+
return [3 /*break*/, 5];
|
|
3356
|
+
case 3:
|
|
3357
|
+
error_2 = _a.sent();
|
|
3358
|
+
err("Failed to initialize new session: ".concat(error_2));
|
|
3359
|
+
this.stopTyping();
|
|
3360
|
+
this.setConnectionStatus("offline");
|
|
3361
|
+
// Show a failure message but don't block - user can still try sending messages
|
|
3362
|
+
this.sendFailureMessage(0, 0, this.getErrorMessage(error_2));
|
|
3363
|
+
return [3 /*break*/, 5];
|
|
3364
|
+
case 4:
|
|
3365
|
+
this.isInitializingSession = false;
|
|
3366
|
+
// Resolve the promise so any waiting postMessage calls can proceed
|
|
3367
|
+
if (this.resolveSessionInit) {
|
|
3368
|
+
this.resolveSessionInit();
|
|
3369
|
+
this.resolveSessionInit = undefined;
|
|
3370
|
+
}
|
|
3371
|
+
this.sessionInitPromise = undefined;
|
|
3372
|
+
return [7 /*endfinally*/];
|
|
3373
|
+
case 5: return [2 /*return*/];
|
|
3374
|
+
}
|
|
3375
|
+
});
|
|
3376
|
+
});
|
|
3377
|
+
};
|
|
3378
|
+
MCPChat.prototype.setConnectionStatus = function (status) {
|
|
3379
|
+
log("SERVER: connection_update: ".concat(JSON.stringify(status)));
|
|
3380
|
+
this.dispatch(setConnectionStatus(status));
|
|
3381
|
+
};
|
|
3382
|
+
MCPChat.prototype.setAccountStatus = function (status) {
|
|
3383
|
+
log("SERVER: account_status: ".concat(JSON.stringify(status)));
|
|
3384
|
+
this.dispatch(setAccountStatus(status));
|
|
3385
|
+
};
|
|
3386
|
+
MCPChat.prototype.getEndpoint = function () {
|
|
3387
|
+
// Normalize serverUrl to remove trailing slashes
|
|
3388
|
+
var serverUrl = this.config.serverUrl.replace(/\/+$/, "");
|
|
3389
|
+
var mode = this.config.mode || "homeowner";
|
|
3390
|
+
if (mode === "business_owner") {
|
|
3391
|
+
return "".concat(serverUrl).concat(BUSINESS_OWNER_ENDPOINT_PATH);
|
|
3392
|
+
}
|
|
3393
|
+
// homeowner mode - serverUrl should already include appId
|
|
3394
|
+
return "".concat(serverUrl).concat(HOMEOWNER_ENDPOINT_PATH);
|
|
3395
|
+
};
|
|
3396
|
+
MCPChat.prototype.sendNewMessage = function (msg, user) {
|
|
3397
|
+
log("SERVER: new message: ".concat(JSON.stringify(msg)));
|
|
3398
|
+
this.dispatch({
|
|
3399
|
+
type: "chat",
|
|
3400
|
+
detail: {
|
|
3401
|
+
type: "chat.msg",
|
|
3402
|
+
user: user || this.getBot(undefined),
|
|
3403
|
+
msg: msg,
|
|
3404
|
+
timestamp: +new Date(),
|
|
3405
|
+
},
|
|
3406
|
+
});
|
|
3407
|
+
};
|
|
3408
|
+
MCPChat.prototype.userJoined = function (user) {
|
|
3409
|
+
log("SERVER: user joined: ".concat(JSON.stringify(user)));
|
|
3410
|
+
this.dispatch({
|
|
3411
|
+
type: "chat",
|
|
3412
|
+
detail: {
|
|
3413
|
+
type: "chat.memberjoin",
|
|
3414
|
+
user: user,
|
|
3415
|
+
timestamp: +new Date(),
|
|
3416
|
+
},
|
|
3417
|
+
});
|
|
3418
|
+
};
|
|
3419
|
+
MCPChat.prototype.typing = function () {
|
|
3420
|
+
this.dispatch({
|
|
3421
|
+
type: "chat",
|
|
3422
|
+
detail: {
|
|
3423
|
+
type: "chat.typing",
|
|
3424
|
+
user: this.getBot(undefined),
|
|
3425
|
+
typing: true,
|
|
3426
|
+
timestamp: +new Date(),
|
|
3427
|
+
},
|
|
3428
|
+
});
|
|
3429
|
+
};
|
|
3430
|
+
MCPChat.prototype.stopTyping = function () {
|
|
3431
|
+
this.dispatch({
|
|
3432
|
+
type: "chat",
|
|
3433
|
+
detail: {
|
|
3434
|
+
type: "chat.typing",
|
|
3435
|
+
user: this.getBot(undefined),
|
|
3436
|
+
typing: false,
|
|
3437
|
+
timestamp: +new Date(),
|
|
3438
|
+
},
|
|
3439
|
+
});
|
|
3440
|
+
};
|
|
3441
|
+
MCPChat.prototype.sendFailureMessage = function (retry, delay, text) {
|
|
3442
|
+
this.stopTyping();
|
|
3443
|
+
this.dispatch({
|
|
3444
|
+
type: "chat",
|
|
3445
|
+
detail: {
|
|
3446
|
+
type: "chat.failureMsg",
|
|
3447
|
+
user: this.getBot(undefined),
|
|
3448
|
+
failureMsg: {
|
|
3449
|
+
retry: retry,
|
|
3450
|
+
delay: delay,
|
|
3451
|
+
text: text,
|
|
3452
|
+
},
|
|
3453
|
+
timestamp: +new Date(),
|
|
3454
|
+
},
|
|
3455
|
+
});
|
|
3456
|
+
};
|
|
3457
|
+
/**
|
|
3458
|
+
* Gets the retry configuration, using custom config if provided or defaults.
|
|
3459
|
+
*/
|
|
3460
|
+
MCPChat.prototype.getRetryConfig = function () {
|
|
3461
|
+
var _a, _b;
|
|
3462
|
+
if (((_b = (_a = this.configurableMessages) === null || _a === void 0 ? void 0 : _a.items) === null || _b === void 0 ? void 0 : _b.length) > 0) {
|
|
3463
|
+
return getConfigurableMessagesConfig(this.configurableMessages);
|
|
3464
|
+
}
|
|
3465
|
+
return DEFAULT_MCP_RETRY_CONFIG;
|
|
3466
|
+
};
|
|
3467
|
+
/**
|
|
3468
|
+
* Delays execution for the specified number of seconds.
|
|
3469
|
+
*/
|
|
3470
|
+
MCPChat.prototype.delay = function (seconds) {
|
|
3471
|
+
return new Promise(function (resolve) { return setTimeout(resolve, seconds * 1000); });
|
|
3472
|
+
};
|
|
3473
|
+
/**
|
|
3474
|
+
* Determines a user-friendly error message based on the error type.
|
|
3475
|
+
*/
|
|
3476
|
+
MCPChat.prototype.getErrorMessage = function (error, statusCode) {
|
|
3477
|
+
// Auth errors
|
|
3478
|
+
if (statusCode === 401) {
|
|
3479
|
+
return "Authentication failed. Please check your credentials.";
|
|
3480
|
+
}
|
|
3481
|
+
if (statusCode === 403) {
|
|
3482
|
+
return "Access denied. You don't have permission to access this resource.";
|
|
3483
|
+
}
|
|
3484
|
+
// Server errors
|
|
3485
|
+
if (statusCode && statusCode >= 500) {
|
|
3486
|
+
return "The server is experiencing issues. Please try again later.";
|
|
3487
|
+
}
|
|
3488
|
+
// Not found (likely wrong appId or endpoint)
|
|
3489
|
+
if (statusCode === 404) {
|
|
3490
|
+
return "Service not found. Please check your configuration.";
|
|
3491
|
+
}
|
|
3492
|
+
// Network errors
|
|
3493
|
+
if (error instanceof TypeError && (error.message.includes("fetch") || error.message.includes("network"))) {
|
|
3494
|
+
return "Unable to connect to the server. Please check your internet connection.";
|
|
3495
|
+
}
|
|
3496
|
+
// Connection refused / server down
|
|
3497
|
+
if (error instanceof Error) {
|
|
3498
|
+
var msg = error.message.toLowerCase();
|
|
3499
|
+
if (msg.includes("failed to fetch") || msg.includes("network") || msg.includes("econnrefused")) {
|
|
3500
|
+
return "Unable to reach the server. Please try again later.";
|
|
3501
|
+
}
|
|
3502
|
+
}
|
|
3503
|
+
// Generic fallback
|
|
3504
|
+
return "Something went wrong. Please try again.";
|
|
3505
|
+
};
|
|
3506
|
+
MCPChat.prototype.dispatchStreamStart = function (messageId) {
|
|
3507
|
+
var bot = this.getBot(undefined);
|
|
3508
|
+
var streamTimestamp = +new Date();
|
|
3509
|
+
log("[dispatchStreamStart] Stream started: ".concat(messageId, ", timestamp: ").concat(streamTimestamp));
|
|
3510
|
+
this.dispatch({
|
|
3511
|
+
type: "chat",
|
|
3512
|
+
detail: {
|
|
3513
|
+
type: "chat.stream.start",
|
|
3514
|
+
user: bot,
|
|
3515
|
+
messageId: messageId,
|
|
3516
|
+
timestamp: streamTimestamp,
|
|
3517
|
+
},
|
|
3518
|
+
});
|
|
3519
|
+
};
|
|
3520
|
+
MCPChat.prototype.dispatchStreamChunk = function (messageId, text) {
|
|
3521
|
+
this.dispatch({
|
|
3522
|
+
type: "chat",
|
|
3523
|
+
detail: {
|
|
3524
|
+
type: "chat.stream.chunk",
|
|
3525
|
+
user: this.getBot(undefined),
|
|
3526
|
+
messageId: messageId,
|
|
3527
|
+
text: text,
|
|
3528
|
+
timestamp: +new Date(),
|
|
3529
|
+
},
|
|
3530
|
+
});
|
|
3531
|
+
};
|
|
3532
|
+
MCPChat.prototype.dispatchStreamEnd = function (messageId, fullMessage, toolCalls) {
|
|
3533
|
+
log("Stream ended: ".concat(messageId));
|
|
3534
|
+
this.dispatch({
|
|
3535
|
+
type: "chat",
|
|
3536
|
+
detail: {
|
|
3537
|
+
type: "chat.stream.end",
|
|
3538
|
+
user: this.getBot(undefined),
|
|
3539
|
+
messageId: messageId,
|
|
3540
|
+
msg: fullMessage,
|
|
3541
|
+
toolCalls: toolCalls,
|
|
3542
|
+
timestamp: +new Date(),
|
|
3543
|
+
},
|
|
3544
|
+
});
|
|
3545
|
+
};
|
|
3546
|
+
MCPChat.prototype.dispatchToolStart = function (params) {
|
|
3547
|
+
this.dispatch({
|
|
3548
|
+
type: "chat",
|
|
3549
|
+
detail: {
|
|
3550
|
+
type: "chat.stream.tool_start",
|
|
3551
|
+
user: this.getBot(undefined),
|
|
3552
|
+
messageId: params.messageId,
|
|
3553
|
+
toolName: params.toolName,
|
|
3554
|
+
toolCallId: params.toolCallId,
|
|
3555
|
+
displayName: params.displayName,
|
|
3556
|
+
label: params.label,
|
|
3557
|
+
hidden: params.hidden,
|
|
3558
|
+
toolInput: params.toolInput,
|
|
3559
|
+
timestamp: +new Date(),
|
|
3560
|
+
},
|
|
3561
|
+
});
|
|
3562
|
+
};
|
|
3563
|
+
MCPChat.prototype.dispatchToolEnd = function (params) {
|
|
3564
|
+
this.dispatch({
|
|
3565
|
+
type: "chat",
|
|
3566
|
+
detail: {
|
|
3567
|
+
type: "chat.stream.tool_end",
|
|
3568
|
+
user: this.getBot(undefined),
|
|
3569
|
+
messageId: params.messageId,
|
|
3570
|
+
toolName: params.toolName,
|
|
3571
|
+
toolCallId: params.toolCallId,
|
|
3572
|
+
displayName: params.displayName,
|
|
3573
|
+
label: params.label,
|
|
3574
|
+
hidden: params.hidden,
|
|
3575
|
+
toolResult: params.toolResult,
|
|
3576
|
+
toolError: params.toolError,
|
|
3577
|
+
timestamp: +new Date(),
|
|
3578
|
+
},
|
|
3579
|
+
});
|
|
3580
|
+
};
|
|
3581
|
+
/**
|
|
3582
|
+
* Starts the throttled streaming for a message.
|
|
3583
|
+
* Text will be dispatched at a readable pace.
|
|
3584
|
+
*/
|
|
3585
|
+
MCPChat.prototype.startStreamThrottle = function (messageId) {
|
|
3586
|
+
var _this = this;
|
|
3587
|
+
this.streamBuffer = "";
|
|
3588
|
+
this.streamingMessageIdForThrottle = messageId;
|
|
3589
|
+
// Clear any existing timer and visibility handler
|
|
3590
|
+
if (this.streamThrottleTimer) {
|
|
3591
|
+
clearInterval(this.streamThrottleTimer);
|
|
3592
|
+
}
|
|
3593
|
+
if (this.visibilityHandler) {
|
|
3594
|
+
document.removeEventListener("visibilitychange", this.visibilityHandler);
|
|
3595
|
+
}
|
|
3596
|
+
// Start interval to dispatch buffered text
|
|
3597
|
+
this.streamThrottleTimer = setInterval(function () {
|
|
3598
|
+
if (_this.streamBuffer.length > 0 && _this.streamingMessageIdForThrottle) {
|
|
3599
|
+
var charsToSend = Math.min(STREAM_CHARS_PER_TICK, _this.streamBuffer.length);
|
|
3600
|
+
var textToDispatch = _this.streamBuffer.slice(0, charsToSend);
|
|
3601
|
+
_this.streamBuffer = _this.streamBuffer.slice(charsToSend);
|
|
3602
|
+
_this.dispatchStreamChunk(_this.streamingMessageIdForThrottle, textToDispatch);
|
|
3603
|
+
}
|
|
3604
|
+
}, STREAM_TICK_INTERVAL_MS);
|
|
3605
|
+
// Handle tab visibility changes - flush buffer when tab becomes visible
|
|
3606
|
+
this.visibilityHandler = function () {
|
|
3607
|
+
if (document.visibilityState === "visible") {
|
|
3608
|
+
log("[Visibility] Tab became visible, buffer length: ".concat(_this.streamBuffer.length, ", messageId: ").concat(_this.streamingMessageIdForThrottle));
|
|
3609
|
+
if (_this.streamBuffer.length > 0 && _this.streamingMessageIdForThrottle) {
|
|
3610
|
+
// Dispatch all buffered text immediately when tab becomes visible
|
|
3611
|
+
log("[Visibility] Flushing ".concat(_this.streamBuffer.length, " chars to UI"));
|
|
3612
|
+
_this.dispatchStreamChunk(_this.streamingMessageIdForThrottle, _this.streamBuffer);
|
|
3613
|
+
_this.streamBuffer = "";
|
|
3614
|
+
}
|
|
3615
|
+
}
|
|
3616
|
+
else {
|
|
3617
|
+
log("[Visibility] Tab hidden");
|
|
3618
|
+
}
|
|
3619
|
+
};
|
|
3620
|
+
document.addEventListener("visibilitychange", this.visibilityHandler);
|
|
3621
|
+
};
|
|
3622
|
+
/**
|
|
3623
|
+
* Adds text to the stream buffer for throttled dispatch.
|
|
3624
|
+
*/
|
|
3625
|
+
MCPChat.prototype.bufferStreamText = function (text) {
|
|
3626
|
+
this.streamBuffer += text;
|
|
3627
|
+
};
|
|
3628
|
+
/**
|
|
3629
|
+
* Flushes any remaining buffered text and stops the throttle timer.
|
|
3630
|
+
* Returns a promise that resolves when all buffered text has been dispatched.
|
|
3631
|
+
*/
|
|
3632
|
+
MCPChat.prototype.flushStreamBuffer = function () {
|
|
3633
|
+
return __awaiter$1(this, void 0, void 0, function () {
|
|
3634
|
+
var waitCount, maxWait;
|
|
3635
|
+
return __generator$1(this, function (_a) {
|
|
3636
|
+
switch (_a.label) {
|
|
3637
|
+
case 0:
|
|
3638
|
+
if (!(this.streamBuffer.length > 0 && this.streamingMessageIdForThrottle)) return [3 /*break*/, 5];
|
|
3639
|
+
if (!(document.visibilityState === "hidden")) return [3 /*break*/, 1];
|
|
3640
|
+
log("[flushStreamBuffer] Tab hidden, dispatching remaining ".concat(this.streamBuffer.length, " chars immediately"));
|
|
3641
|
+
this.dispatchStreamChunk(this.streamingMessageIdForThrottle, this.streamBuffer);
|
|
3642
|
+
this.streamBuffer = "";
|
|
3643
|
+
return [3 /*break*/, 5];
|
|
3644
|
+
case 1:
|
|
3645
|
+
waitCount = 0;
|
|
3646
|
+
maxWait = 100;
|
|
3647
|
+
_a.label = 2;
|
|
3648
|
+
case 2:
|
|
3649
|
+
if (!(this.streamBuffer.length > 0 && waitCount < maxWait)) return [3 /*break*/, 4];
|
|
3650
|
+
return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, STREAM_TICK_INTERVAL_MS); })];
|
|
3651
|
+
case 3:
|
|
3652
|
+
_a.sent();
|
|
3653
|
+
waitCount++;
|
|
3654
|
+
return [3 /*break*/, 2];
|
|
3655
|
+
case 4:
|
|
3656
|
+
// If still has content after timeout, flush it
|
|
3657
|
+
if (this.streamBuffer.length > 0 && this.streamingMessageIdForThrottle) {
|
|
3658
|
+
log("[flushStreamBuffer] Timeout waiting for buffer, dispatching remaining ".concat(this.streamBuffer.length, " chars"));
|
|
3659
|
+
this.dispatchStreamChunk(this.streamingMessageIdForThrottle, this.streamBuffer);
|
|
3660
|
+
this.streamBuffer = "";
|
|
3661
|
+
}
|
|
3662
|
+
_a.label = 5;
|
|
3663
|
+
case 5:
|
|
3664
|
+
// Stop the throttle timer
|
|
3665
|
+
if (this.streamThrottleTimer) {
|
|
3666
|
+
clearInterval(this.streamThrottleTimer);
|
|
3667
|
+
this.streamThrottleTimer = undefined;
|
|
3668
|
+
}
|
|
3669
|
+
if (this.visibilityHandler) {
|
|
3670
|
+
document.removeEventListener("visibilitychange", this.visibilityHandler);
|
|
3671
|
+
this.visibilityHandler = undefined;
|
|
3672
|
+
}
|
|
3673
|
+
this.streamingMessageIdForThrottle = undefined;
|
|
3674
|
+
return [2 /*return*/];
|
|
3675
|
+
}
|
|
3676
|
+
});
|
|
3677
|
+
});
|
|
3678
|
+
};
|
|
3679
|
+
/**
|
|
3680
|
+
* Immediately stops throttling and clears the buffer (used on abort/error).
|
|
3681
|
+
*/
|
|
3682
|
+
MCPChat.prototype.stopStreamThrottle = function () {
|
|
3683
|
+
if (this.streamThrottleTimer) {
|
|
3684
|
+
clearInterval(this.streamThrottleTimer);
|
|
3685
|
+
this.streamThrottleTimer = undefined;
|
|
3686
|
+
}
|
|
3687
|
+
if (this.visibilityHandler) {
|
|
3688
|
+
document.removeEventListener("visibilitychange", this.visibilityHandler);
|
|
3689
|
+
this.visibilityHandler = undefined;
|
|
3690
|
+
}
|
|
3691
|
+
this.streamBuffer = "";
|
|
3692
|
+
this.streamingMessageIdForThrottle = undefined;
|
|
3693
|
+
};
|
|
3694
|
+
MCPChat.prototype.sendOfflineMsg = function (_, cb) {
|
|
3695
|
+
cb();
|
|
3696
|
+
};
|
|
3697
|
+
MCPChat.prototype.sendChatMsg = function (message, cb) {
|
|
3698
|
+
return __awaiter$1(this, void 0, void 0, function () {
|
|
3699
|
+
return __generator$1(this, function (_a) {
|
|
3700
|
+
return [2 /*return*/, this.sendChatMsgRequest({
|
|
3701
|
+
msg: message,
|
|
3702
|
+
timestamp: new Date().getTime(),
|
|
3703
|
+
agent: false,
|
|
3704
|
+
user: undefined,
|
|
3705
|
+
type: "msg",
|
|
3706
|
+
}, cb)];
|
|
3707
|
+
});
|
|
3708
|
+
});
|
|
3709
|
+
};
|
|
3710
|
+
MCPChat.prototype.bargeOut = function (_) {
|
|
3711
|
+
return __awaiter$1(this, void 0, void 0, function () {
|
|
3712
|
+
return __generator$1(this, function (_a) {
|
|
3713
|
+
throw new Error("bargeOut is not supported in MCP mode");
|
|
3714
|
+
});
|
|
3715
|
+
});
|
|
3716
|
+
};
|
|
3717
|
+
MCPChat.prototype.bargeIn = function (_agentName, __) {
|
|
3718
|
+
return __awaiter$1(this, void 0, void 0, function () {
|
|
3719
|
+
return __generator$1(this, function (_a) {
|
|
3720
|
+
throw new Error("bargeIn is not supported in MCP mode");
|
|
3721
|
+
});
|
|
3722
|
+
});
|
|
3723
|
+
};
|
|
3724
|
+
/**
|
|
3725
|
+
* Ensures a nick has the "agent:" prefix required for agent message detection.
|
|
3726
|
+
*/
|
|
3727
|
+
MCPChat.prototype.ensureAgentNick = function (nick) {
|
|
3728
|
+
if (!nick)
|
|
3729
|
+
return "agent:robot";
|
|
3730
|
+
if (nick.startsWith("agent:"))
|
|
3731
|
+
return nick;
|
|
3732
|
+
return "agent:".concat(nick);
|
|
3733
|
+
};
|
|
3734
|
+
MCPChat.prototype.getBot = function (user) {
|
|
3735
|
+
var _a;
|
|
3736
|
+
var bot = (_a = this.options) === null || _a === void 0 ? void 0 : _a.bot;
|
|
3737
|
+
if (!user) {
|
|
3738
|
+
// Ensure bot nick has agent: prefix and mark as AI
|
|
3739
|
+
var defaultBot = bot || { nick: "agent:robot", displayName: "Assistant" };
|
|
3740
|
+
return __assign(__assign({}, defaultBot), { nick: this.ensureAgentNick(defaultBot.nick), isAI: true });
|
|
3741
|
+
}
|
|
3742
|
+
if (user.displayName && user.nick && user.avatarPath) {
|
|
3743
|
+
return __assign(__assign({}, user), { nick: this.ensureAgentNick(user.nick), isAI: true });
|
|
3744
|
+
}
|
|
3745
|
+
return __assign(__assign({}, user), { nick: this.ensureAgentNick(user.nick || (bot === null || bot === void 0 ? void 0 : bot.nick)), displayName: user.displayName || (bot === null || bot === void 0 ? void 0 : bot.displayName) || "Assistant", avatarPath: user.avatarPath || (bot === null || bot === void 0 ? void 0 : bot.avatarPath), isAI: true });
|
|
3746
|
+
};
|
|
3747
|
+
MCPChat.prototype.sendChatMsgRequest = function (serviceRequest, cb) {
|
|
3748
|
+
return __awaiter$1(this, void 0, void 0, function () {
|
|
3749
|
+
var error_3;
|
|
3750
|
+
return __generator$1(this, function (_a) {
|
|
3751
|
+
switch (_a.label) {
|
|
3752
|
+
case 0:
|
|
3753
|
+
_a.trys.push([0, 2, , 3]);
|
|
3754
|
+
return [4 /*yield*/, this.postMessage(serviceRequest)];
|
|
3755
|
+
case 1:
|
|
3756
|
+
_a.sent();
|
|
3757
|
+
cb();
|
|
3758
|
+
return [3 /*break*/, 3];
|
|
3759
|
+
case 2:
|
|
3760
|
+
error_3 = _a.sent();
|
|
3761
|
+
cb(error_3);
|
|
3762
|
+
return [3 /*break*/, 3];
|
|
3763
|
+
case 3: return [2 /*return*/];
|
|
3764
|
+
}
|
|
3765
|
+
});
|
|
3766
|
+
});
|
|
3767
|
+
};
|
|
3768
|
+
MCPChat.prototype.postMessage = function (message) {
|
|
3769
|
+
return __awaiter$1(this, void 0, void 0, function () {
|
|
3770
|
+
var trimmedText, timeout, result, retryConfig, messageText, success, attempt, isLastAttempt, config, error_4, statusCode, errorMessage, statusCode_1, errorMessage, nextConfig, nextConfig;
|
|
3771
|
+
var _a, _b, _c;
|
|
3772
|
+
return __generator$1(this, function (_d) {
|
|
3773
|
+
switch (_d.label) {
|
|
3774
|
+
case 0:
|
|
3775
|
+
trimmedText = ((_b = (_a = message.msg) === null || _a === void 0 ? void 0 : _a.text) === null || _b === void 0 ? void 0 : _b.trim()) || "";
|
|
3776
|
+
if (!trimmedText) {
|
|
3777
|
+
log("Ignoring empty message");
|
|
3778
|
+
return [2 /*return*/];
|
|
3779
|
+
}
|
|
3780
|
+
if (!(this.isInitializingSession && this.sessionInitPromise)) return [3 /*break*/, 2];
|
|
3781
|
+
log("Waiting for session initialization to complete before sending message...");
|
|
3782
|
+
timeout = new Promise(function (resolve) {
|
|
3783
|
+
return setTimeout(function () { return resolve("timeout"); }, 10000);
|
|
3784
|
+
});
|
|
3785
|
+
return [4 /*yield*/, Promise.race([this.sessionInitPromise, timeout])];
|
|
3786
|
+
case 1:
|
|
3787
|
+
result = _d.sent();
|
|
3788
|
+
if (result === "timeout" || this.isInitializingSession) {
|
|
3789
|
+
err("Session initialization timed out");
|
|
3790
|
+
this.sendFailureMessage(0, 0, "Chat is not ready. Please try again.");
|
|
3791
|
+
return [2 /*return*/];
|
|
3792
|
+
}
|
|
3793
|
+
_d.label = 2;
|
|
3794
|
+
case 2:
|
|
3795
|
+
retryConfig = this.getRetryConfig();
|
|
3796
|
+
messageText = ((_c = message.msg) === null || _c === void 0 ? void 0 : _c.text) || "";
|
|
3797
|
+
success = false;
|
|
3798
|
+
attempt = 0;
|
|
3799
|
+
_d.label = 3;
|
|
3800
|
+
case 3:
|
|
3801
|
+
if (!(attempt < retryConfig.length)) return [3 /*break*/, 14];
|
|
3802
|
+
isLastAttempt = attempt === retryConfig.length - 1;
|
|
3803
|
+
config = retryConfig[attempt];
|
|
3804
|
+
log("MCP attempt ".concat(attempt + 1, "/").concat(retryConfig.length, " for message: ").concat(messageText));
|
|
3805
|
+
// Start typing indicator
|
|
3806
|
+
this.typing();
|
|
3807
|
+
_d.label = 4;
|
|
3808
|
+
case 4:
|
|
3809
|
+
_d.trys.push([4, 6, , 13]);
|
|
3810
|
+
return [4 /*yield*/, this.attemptSSERequest(message)];
|
|
3811
|
+
case 5:
|
|
3812
|
+
_d.sent();
|
|
3813
|
+
success = true;
|
|
3814
|
+
return [3 /*break*/, 14]; // Success - exit retry loop
|
|
3815
|
+
case 6:
|
|
3816
|
+
error_4 = _d.sent();
|
|
3817
|
+
this.stopTyping();
|
|
3818
|
+
statusCode = error_4 === null || error_4 === void 0 ? void 0 : error_4.statusCode;
|
|
3819
|
+
err("MCP attempt ".concat(attempt + 1, " failed: ").concat(error_4, ", statusCode: ").concat(statusCode));
|
|
3820
|
+
log("[postMessage] Error details:", { error: error_4, statusCode: statusCode, messageText: messageText, sessionId: this._sessionId });
|
|
3821
|
+
// Don't retry on 4xx client errors - these won't succeed on retry
|
|
3822
|
+
if (statusCode && statusCode >= 400 && statusCode < 500) {
|
|
3823
|
+
log("Client error ".concat(statusCode, ", not retrying"));
|
|
3824
|
+
errorMessage = this.getErrorMessage(error_4, statusCode);
|
|
3825
|
+
this.sendFailureMessage(0, 0, errorMessage);
|
|
3826
|
+
return [2 /*return*/];
|
|
3827
|
+
}
|
|
3828
|
+
// Set offline status
|
|
3829
|
+
this.setConnectionStatus("offline");
|
|
3830
|
+
if (!isLastAttempt) return [3 /*break*/, 7];
|
|
3831
|
+
statusCode_1 = error_4 === null || error_4 === void 0 ? void 0 : error_4.statusCode;
|
|
3832
|
+
errorMessage = config.text || this.getErrorMessage(error_4, statusCode_1);
|
|
3833
|
+
this.sendFailureMessage(0, 0, errorMessage);
|
|
3834
|
+
return [3 /*break*/, 12];
|
|
3835
|
+
case 7:
|
|
3836
|
+
if (!(attempt > 0 || config.text)) return [3 /*break*/, 10];
|
|
3837
|
+
nextConfig = retryConfig[attempt + 1];
|
|
3838
|
+
if (!(nextConfig && nextConfig.delay > 0)) return [3 /*break*/, 9];
|
|
3839
|
+
this.sendFailureMessage(nextConfig.retry, nextConfig.delay, nextConfig.text);
|
|
3840
|
+
// Wait for the delay before retrying
|
|
3841
|
+
return [4 /*yield*/, this.delay(nextConfig.delay)];
|
|
3842
|
+
case 8:
|
|
3843
|
+
// Wait for the delay before retrying
|
|
3844
|
+
_d.sent();
|
|
3845
|
+
_d.label = 9;
|
|
3846
|
+
case 9: return [3 /*break*/, 12];
|
|
3847
|
+
case 10:
|
|
3848
|
+
nextConfig = retryConfig[attempt + 1];
|
|
3849
|
+
if (!(nextConfig && nextConfig.delay > 0)) return [3 /*break*/, 12];
|
|
3850
|
+
return [4 /*yield*/, this.delay(nextConfig.delay)];
|
|
3851
|
+
case 11:
|
|
3852
|
+
_d.sent();
|
|
3853
|
+
_d.label = 12;
|
|
3854
|
+
case 12: return [3 /*break*/, 13];
|
|
3855
|
+
case 13:
|
|
3856
|
+
attempt++;
|
|
3857
|
+
return [3 /*break*/, 3];
|
|
3858
|
+
case 14:
|
|
3859
|
+
if (!success) {
|
|
3860
|
+
log("All MCP retry attempts exhausted");
|
|
3861
|
+
}
|
|
3862
|
+
return [2 /*return*/];
|
|
3863
|
+
}
|
|
3864
|
+
});
|
|
3865
|
+
});
|
|
3866
|
+
};
|
|
3867
|
+
/**
|
|
3868
|
+
* Attempts a single SSE request. Throws on failure.
|
|
3869
|
+
*/
|
|
3870
|
+
MCPChat.prototype.attemptSSERequest = function (message) {
|
|
3871
|
+
return __awaiter$1(this, void 0, void 0, function () {
|
|
3872
|
+
var messageText;
|
|
3873
|
+
var _a;
|
|
3874
|
+
return __generator$1(this, function (_b) {
|
|
3875
|
+
messageText = ((_a = message.msg) === null || _a === void 0 ? void 0 : _a.text) || "";
|
|
3876
|
+
log("[attemptSSERequest] Sending message: \"".concat(messageText, "\", userId: ").concat(this._userId, ", sessionId: ").concat(this._sessionId));
|
|
3877
|
+
return [2 /*return*/, this.performSSERequest(this.buildRequestPayload({ message: messageText }))];
|
|
3878
|
+
});
|
|
3879
|
+
});
|
|
3880
|
+
};
|
|
3881
|
+
/**
|
|
3882
|
+
* Performs an SSE request with the given body.
|
|
3883
|
+
* Used for both new session initialization and regular messages.
|
|
3884
|
+
*/
|
|
3885
|
+
MCPChat.prototype.performSSERequest = function (body) {
|
|
3886
|
+
return __awaiter$1(this, void 0, void 0, function () {
|
|
3887
|
+
var endpoint, messageId, textChunks, hasError, httpStatusCode, sseError;
|
|
3888
|
+
var _this = this;
|
|
3889
|
+
return __generator$1(this, function (_a) {
|
|
3890
|
+
endpoint = this.getEndpoint();
|
|
3891
|
+
log("SSE request to ".concat(endpoint, ": ").concat(JSON.stringify(body)));
|
|
3892
|
+
// Cancel any ongoing stream
|
|
3893
|
+
if (this.abortController) {
|
|
3894
|
+
this.abortController.abort();
|
|
3895
|
+
this.abortController = undefined;
|
|
3896
|
+
}
|
|
3897
|
+
this.abortController = new AbortController();
|
|
3898
|
+
messageId = uuid_1();
|
|
3899
|
+
this.currentMessageId = messageId;
|
|
3900
|
+
textChunks = [];
|
|
3901
|
+
hasError = false;
|
|
3902
|
+
return [2 /*return*/, new Promise(function (resolve, reject) {
|
|
3903
|
+
// WARNING: Do not log headers object - it contains sensitive auth tokens
|
|
3904
|
+
cjs.fetchEventSource(endpoint, {
|
|
3905
|
+
method: "POST",
|
|
3906
|
+
headers: __assign({ "Content-Type": "application/json" }, ((_this.config.mode || "homeowner") === "business_owner" && _this.config.authToken
|
|
3907
|
+
? { Authorization: "Bearer ".concat(_this.config.authToken) }
|
|
3908
|
+
: {})),
|
|
3909
|
+
body: JSON.stringify(body),
|
|
3910
|
+
signal: _this.abortController.signal,
|
|
3911
|
+
onopen: function (response) { return __awaiter$1(_this, void 0, void 0, function () {
|
|
3912
|
+
var error;
|
|
3913
|
+
return __generator$1(this, function (_a) {
|
|
3914
|
+
httpStatusCode = response.status;
|
|
3915
|
+
if (!response.ok) {
|
|
3916
|
+
error = new Error("HTTP error! status: ".concat(response.status));
|
|
3917
|
+
error.statusCode = response.status;
|
|
3918
|
+
throw error;
|
|
3919
|
+
}
|
|
3920
|
+
log("SSE connection opened");
|
|
3921
|
+
// Connection successful - ensure we're marked as online
|
|
3922
|
+
this.setConnectionStatus("online");
|
|
3923
|
+
return [2 /*return*/];
|
|
3924
|
+
});
|
|
3925
|
+
}); },
|
|
3926
|
+
onmessage: function (event) {
|
|
3927
|
+
log("SSE event: ".concat(event.event, ", data: ").concat(event.data));
|
|
3928
|
+
var eventType = event.event || "message";
|
|
3929
|
+
var data = {};
|
|
3930
|
+
try {
|
|
3931
|
+
data = JSON.parse(event.data);
|
|
3932
|
+
}
|
|
3933
|
+
catch (e) {
|
|
3934
|
+
log("Failed to parse SSE data: ".concat(e));
|
|
3935
|
+
return;
|
|
3936
|
+
}
|
|
3937
|
+
// Track sequence number for replay on reconnect
|
|
3938
|
+
if (data.seq !== undefined && data.seq !== null) {
|
|
3939
|
+
_this.lastSeq = data.seq;
|
|
3940
|
+
if (_this._sessionId) {
|
|
3941
|
+
storeLastSeq(_this._sessionId, data.seq);
|
|
3942
|
+
}
|
|
3943
|
+
}
|
|
3944
|
+
switch (eventType) {
|
|
3945
|
+
case "connected":
|
|
3946
|
+
// Connection established
|
|
3947
|
+
log("[SSE] Connected event received, messageId: ".concat(messageId, ", isReturningUser: ").concat(data.isReturningUser, ", isReplay: ").concat(data.isReplay));
|
|
3948
|
+
// Track if this is a replay connection
|
|
3949
|
+
if (data.isReplay) {
|
|
3950
|
+
_this.isReplaying = true;
|
|
3951
|
+
}
|
|
3952
|
+
// Store identity from server response
|
|
3953
|
+
if (data.userId || data.sessionId) {
|
|
3954
|
+
if (data.userId) {
|
|
3955
|
+
_this._userId = data.userId;
|
|
3956
|
+
_this.userIdIsServerAssigned = true;
|
|
3957
|
+
}
|
|
3958
|
+
if (data.sessionId) {
|
|
3959
|
+
_this._sessionId = data.sessionId;
|
|
3960
|
+
_this.dispatch(setSessionId(_this._sessionId));
|
|
3961
|
+
}
|
|
3962
|
+
// Persist to localStorage for session continuity
|
|
3963
|
+
storeIdentity(data.userId, data.sessionId);
|
|
3964
|
+
log("[SSE] Identity from connected - userId: ".concat(data.userId, ", sessionId: ").concat(data.sessionId));
|
|
3965
|
+
}
|
|
3966
|
+
// Always start streaming - server sends greeting for both new and returning users
|
|
3967
|
+
// (resumeSession requests always get a personalized greeting)
|
|
3968
|
+
_this.currentToolCalls = [];
|
|
3969
|
+
_this.stopTyping();
|
|
3970
|
+
_this.dispatchStreamStart(messageId);
|
|
3971
|
+
_this.startStreamThrottle(messageId);
|
|
3972
|
+
break;
|
|
3973
|
+
case "session_expired":
|
|
3974
|
+
// Session no longer exists on server - need to start fresh
|
|
3975
|
+
log("[SSE] Session expired: ".concat(data.reason || "Session not found"));
|
|
3976
|
+
// Clear stored session (and lastSeq) but keep userId for returning user identification
|
|
3977
|
+
clearStoredSession(_this._sessionId);
|
|
3978
|
+
_this._sessionId = "";
|
|
3979
|
+
_this.lastSeq = undefined;
|
|
3980
|
+
_this.stopTyping();
|
|
3981
|
+
_this.stopStreamThrottle();
|
|
3982
|
+
// Reset validation flag before starting new session
|
|
3983
|
+
// (ensures connected handler will dispatch stream for greeting)
|
|
3984
|
+
_this.isValidatingSession = false;
|
|
3985
|
+
// Start a new session automatically
|
|
3986
|
+
_this.initializeNewSession();
|
|
3987
|
+
resolve();
|
|
3988
|
+
break;
|
|
3989
|
+
case "text":
|
|
3990
|
+
if (data.text) {
|
|
3991
|
+
log("[SSE] Text chunk received (".concat(data.text.length, " chars), total chunks: ").concat(textChunks.length + 1, ", isReplay: ").concat(data.isReplay));
|
|
3992
|
+
textChunks.push(data.text);
|
|
3993
|
+
if (data.isReplay) {
|
|
3994
|
+
// Replayed text — dispatch immediately (no throttle)
|
|
3995
|
+
// so the user sees caught-up content instantly
|
|
3996
|
+
_this.dispatchStreamChunk(messageId, data.text);
|
|
3997
|
+
}
|
|
3998
|
+
else {
|
|
3999
|
+
// Live text — buffer for throttled dispatch
|
|
4000
|
+
_this.bufferStreamText(data.text);
|
|
4001
|
+
}
|
|
4002
|
+
}
|
|
4003
|
+
break;
|
|
4004
|
+
case "tool_start":
|
|
4005
|
+
log("Tool execution started: ".concat(data.toolName, ", toolCallId: ").concat(data.toolCallId, ", displayName: ").concat(data.displayName, ", label: ").concat(data.label, ", hidden: ").concat(data.hidden));
|
|
4006
|
+
if (data.toolName) {
|
|
4007
|
+
var toolCall = {
|
|
4008
|
+
name: data.toolName,
|
|
4009
|
+
toolCallId: data.toolCallId,
|
|
4010
|
+
displayName: data.displayName,
|
|
4011
|
+
label: data.label,
|
|
4012
|
+
hidden: data.hidden,
|
|
4013
|
+
input: data.toolInput,
|
|
4014
|
+
startTime: Date.now(),
|
|
4015
|
+
isExecuting: true,
|
|
4016
|
+
};
|
|
4017
|
+
_this.currentToolCalls.push(toolCall);
|
|
4018
|
+
// Always dispatch tool start for user-friendly progress display
|
|
4019
|
+
_this.dispatchToolStart({
|
|
4020
|
+
messageId: messageId,
|
|
4021
|
+
toolName: data.toolName,
|
|
4022
|
+
toolCallId: data.toolCallId,
|
|
4023
|
+
displayName: data.displayName,
|
|
4024
|
+
label: data.label,
|
|
4025
|
+
hidden: data.hidden,
|
|
4026
|
+
toolInput: data.toolInput,
|
|
4027
|
+
});
|
|
4028
|
+
}
|
|
4029
|
+
break;
|
|
4030
|
+
case "tool_end":
|
|
4031
|
+
log("Tool execution ended: ".concat(data.toolName, ", toolCallId: ").concat(data.toolCallId, ", displayName: ").concat(data.displayName, ", label: ").concat(data.label, ", hidden: ").concat(data.hidden));
|
|
4032
|
+
if (data.toolName) {
|
|
4033
|
+
// Find the matching tool call and update it
|
|
4034
|
+
// Match by toolCallId if available, otherwise fall back to name + isExecuting
|
|
4035
|
+
var toolCall = _this.currentToolCalls.find(function (tc) {
|
|
4036
|
+
return data.toolCallId
|
|
4037
|
+
? tc.toolCallId === data.toolCallId
|
|
4038
|
+
: tc.name === data.toolName && tc.isExecuting;
|
|
4039
|
+
});
|
|
4040
|
+
if (toolCall) {
|
|
4041
|
+
toolCall.result = data.toolResult;
|
|
4042
|
+
toolCall.error = data.toolError;
|
|
4043
|
+
toolCall.label = data.label; // Update with completion label
|
|
4044
|
+
toolCall.hidden = data.hidden; // Update hidden status
|
|
4045
|
+
toolCall.endTime = Date.now();
|
|
4046
|
+
toolCall.isExecuting = false;
|
|
4047
|
+
}
|
|
4048
|
+
// Always dispatch tool end for user-friendly progress display
|
|
4049
|
+
_this.dispatchToolEnd({
|
|
4050
|
+
messageId: messageId,
|
|
4051
|
+
toolName: data.toolName,
|
|
4052
|
+
toolCallId: data.toolCallId,
|
|
4053
|
+
displayName: data.displayName,
|
|
4054
|
+
label: data.label,
|
|
4055
|
+
hidden: data.hidden,
|
|
4056
|
+
toolResult: data.toolResult,
|
|
4057
|
+
toolError: data.toolError,
|
|
4058
|
+
});
|
|
4059
|
+
}
|
|
4060
|
+
break;
|
|
4061
|
+
case "done":
|
|
4062
|
+
_this.stopTyping();
|
|
4063
|
+
if (data.response) {
|
|
4064
|
+
// Update session ID if provided and persist to localStorage
|
|
4065
|
+
if (data.response.sessionId) {
|
|
4066
|
+
_this._sessionId = data.response.sessionId;
|
|
4067
|
+
_this.dispatch(setSessionId(_this._sessionId));
|
|
4068
|
+
storeIdentity(undefined, data.response.sessionId);
|
|
4069
|
+
}
|
|
4070
|
+
// Wait for buffer to flush, then finalize
|
|
4071
|
+
var accumulatedText = textChunks.join("");
|
|
4072
|
+
log("[SSE] Done event received. Total chunks: ".concat(textChunks.length, ", accumulated text length: ").concat(accumulatedText.length));
|
|
4073
|
+
log("[SSE] First 100 chars of accumulated: ".concat(accumulatedText.substring(0, 100)));
|
|
4074
|
+
// Map suggestions to options (chips) format
|
|
4075
|
+
var suggestions = data.response.suggestions;
|
|
4076
|
+
var options = suggestions === null || suggestions === void 0 ? void 0 : suggestions.map(function (s) { return ({
|
|
4077
|
+
label: s.title,
|
|
4078
|
+
actionUrl: s.url || "",
|
|
4079
|
+
}); });
|
|
4080
|
+
if (options === null || options === void 0 ? void 0 : options.length) {
|
|
4081
|
+
log("[SSE] Suggestions: ".concat(options.length, " items"));
|
|
4082
|
+
}
|
|
4083
|
+
// Map sources for reference links
|
|
4084
|
+
var sources = data.response.sources;
|
|
4085
|
+
if (sources === null || sources === void 0 ? void 0 : sources.length) {
|
|
4086
|
+
log("[SSE] Sources: ".concat(sources.length, " items"));
|
|
4087
|
+
}
|
|
4088
|
+
var finalMessage_1 = {
|
|
4089
|
+
text: accumulatedText || data.response.response || "",
|
|
4090
|
+
options: options,
|
|
4091
|
+
sources: sources,
|
|
4092
|
+
};
|
|
4093
|
+
// Capture tool calls before flushing (debug mode)
|
|
4094
|
+
var toolCalls_1 = _this.config.debugMode ? __spreadArray$1([], _this.currentToolCalls, true) : undefined;
|
|
4095
|
+
_this.flushStreamBuffer().then(function () {
|
|
4096
|
+
_this.dispatchStreamEnd(messageId, finalMessage_1, toolCalls_1);
|
|
4097
|
+
_this.currentToolCalls = []; // Clear after dispatch
|
|
4098
|
+
resolve();
|
|
4099
|
+
});
|
|
4100
|
+
}
|
|
4101
|
+
else {
|
|
4102
|
+
_this.stopStreamThrottle();
|
|
4103
|
+
resolve();
|
|
4104
|
+
}
|
|
4105
|
+
break;
|
|
4106
|
+
case "replay_complete":
|
|
4107
|
+
_this.isReplaying = false;
|
|
4108
|
+
log("[SSE] Replay complete: replayed ".concat(data.replayedCount, " events, currentSeq: ").concat(data.currentSeq));
|
|
4109
|
+
break;
|
|
4110
|
+
case "replay_partial":
|
|
4111
|
+
_this.isReplaying = false;
|
|
4112
|
+
log("[SSE] Replay partial: some events unavailable, oldestAvailableSeq: ".concat(data.oldestAvailableSeq));
|
|
4113
|
+
break;
|
|
4114
|
+
case "error":
|
|
4115
|
+
hasError = true;
|
|
4116
|
+
_this.stopTyping();
|
|
4117
|
+
_this.stopStreamThrottle();
|
|
4118
|
+
err("SSE error: ".concat(data.error));
|
|
4119
|
+
// Server-side errors should not retry - reject immediately with the error
|
|
4120
|
+
reject(new Error(data.error || "Server error during processing"));
|
|
4121
|
+
break;
|
|
4122
|
+
}
|
|
4123
|
+
},
|
|
4124
|
+
onerror: function (error) {
|
|
4125
|
+
// Only handle error if this is still the current message
|
|
4126
|
+
if (_this.currentMessageId === messageId && !hasError) {
|
|
4127
|
+
hasError = true;
|
|
4128
|
+
_this.isReplaying = false;
|
|
4129
|
+
_this.stopStreamThrottle();
|
|
4130
|
+
sseError = error instanceof Error ? error : new Error(String(error));
|
|
4131
|
+
// Get status code if available
|
|
4132
|
+
var statusCode = (error === null || error === void 0 ? void 0 : error.statusCode) || httpStatusCode;
|
|
4133
|
+
if (statusCode) {
|
|
4134
|
+
sseError.statusCode = statusCode;
|
|
4135
|
+
}
|
|
4136
|
+
err("SSE connection error: ".concat(error));
|
|
4137
|
+
}
|
|
4138
|
+
// Throw to stop fetchEventSource from retrying
|
|
4139
|
+
throw error;
|
|
4140
|
+
},
|
|
4141
|
+
}).catch(function (error) {
|
|
4142
|
+
// Handle errors that occur before/outside the SSE connection
|
|
4143
|
+
_this.stopStreamThrottle();
|
|
4144
|
+
if (!hasError && _this.currentMessageId === messageId) {
|
|
4145
|
+
var statusCode = (error === null || error === void 0 ? void 0 : error.statusCode) || httpStatusCode;
|
|
4146
|
+
var wrappedError = error instanceof Error ? error : new Error(String(error));
|
|
4147
|
+
if (statusCode) {
|
|
4148
|
+
wrappedError.statusCode = statusCode;
|
|
4149
|
+
}
|
|
4150
|
+
reject(wrappedError);
|
|
4151
|
+
}
|
|
4152
|
+
else if (sseError) {
|
|
4153
|
+
reject(sseError);
|
|
4154
|
+
}
|
|
4155
|
+
else {
|
|
4156
|
+
reject(error);
|
|
4157
|
+
}
|
|
4158
|
+
});
|
|
4159
|
+
})];
|
|
4160
|
+
});
|
|
4161
|
+
});
|
|
4162
|
+
};
|
|
4163
|
+
MCPChat.prototype.sendTyping = function () { };
|
|
4164
|
+
MCPChat.prototype.setVisitorInfo = function (visitorInfo, sessionId, cb) {
|
|
4165
|
+
this.visitorInfo = visitorInfo;
|
|
4166
|
+
this._attributes = __assign({}, this.visitorInfo.attributes);
|
|
4167
|
+
// do not set currentUrl if localhost
|
|
4168
|
+
var href = new URL(window.location.href);
|
|
4169
|
+
if (href === null || href === void 0 ? void 0 : href.host.toLowerCase().startsWith("localhost")) {
|
|
4170
|
+
this._attributes.isLocal = true;
|
|
4171
|
+
}
|
|
4172
|
+
else {
|
|
4173
|
+
this._attributes.currentUrl = window.location.href;
|
|
4174
|
+
}
|
|
4175
|
+
this._accessToken = this.visitorInfo.accessToken;
|
|
4176
|
+
// Bot joined is dispatched in init() to ensure correct ordering
|
|
4177
|
+
// Show typing indicator if we're still waiting for initial greeting
|
|
4178
|
+
if (this.isInitializingSession) {
|
|
4179
|
+
this.typing();
|
|
4180
|
+
}
|
|
4181
|
+
this.startSession(sessionId);
|
|
4182
|
+
cb();
|
|
4183
|
+
};
|
|
4184
|
+
MCPChat.prototype.sendChatRating = function () { };
|
|
4185
|
+
MCPChat.prototype.sendFile = function (_, cb) {
|
|
4186
|
+
cb(new Error("File upload not supported in MCP mode"));
|
|
4187
|
+
};
|
|
4188
|
+
MCPChat.prototype.markAsRead = function () { };
|
|
4189
|
+
MCPChat.prototype.flush = function () { };
|
|
4190
|
+
MCPChat.prototype.dispose = function () {
|
|
4191
|
+
this.stopStreamThrottle();
|
|
4192
|
+
if (this.abortController) {
|
|
4193
|
+
this.abortController.abort();
|
|
4194
|
+
this.abortController = undefined;
|
|
4195
|
+
}
|
|
4196
|
+
};
|
|
4197
|
+
MCPChat.prototype.sleep = function () {
|
|
4198
|
+
this.stopStreamThrottle();
|
|
4199
|
+
this.isReplaying = false;
|
|
4200
|
+
if (this.abortController) {
|
|
4201
|
+
this.abortController.abort();
|
|
4202
|
+
this.abortController = undefined;
|
|
4203
|
+
}
|
|
4204
|
+
};
|
|
4205
|
+
MCPChat.prototype.wakeup = function () {
|
|
4206
|
+
// When tab is foregrounded, just restore connection status.
|
|
4207
|
+
// Don't validate/replay yet — the session will be validated
|
|
4208
|
+
// on the next user message. This avoids creating duplicate
|
|
4209
|
+
// greetings if validation fails transiently.
|
|
4210
|
+
if (this._sessionId) {
|
|
4211
|
+
log("[MCPChat] Wakeup - session exists, lastSeq: ".concat(this.lastSeq));
|
|
4212
|
+
this.setConnectionStatus("online");
|
|
4213
|
+
}
|
|
4214
|
+
};
|
|
4215
|
+
MCPChat.prototype.startSession = function (sessionId) {
|
|
4216
|
+
// userId priority:
|
|
4217
|
+
// 1. Existing server-assigned userId (from localStorage or previous connected event)
|
|
4218
|
+
// 2. Explicitly provided visitorId or email from host app
|
|
4219
|
+
// 3. Generate a new one as last resort
|
|
4220
|
+
if (this.userIdIsServerAssigned) {
|
|
4221
|
+
// Keep server-assigned userId — don't overwrite with default fingerprint
|
|
4222
|
+
log("[startSession] Preserving server-assigned userId: ".concat(this._userId));
|
|
4223
|
+
}
|
|
4224
|
+
else if (this.visitorInfo.visitorId) {
|
|
4225
|
+
this._userId = "".concat(this.visitorInfo.visitorId);
|
|
4226
|
+
}
|
|
4227
|
+
else if (this.visitorInfo.email) {
|
|
4228
|
+
this._userId = "mcp-widget-user-".concat(this.visitorInfo.email);
|
|
4229
|
+
}
|
|
4230
|
+
else if (!this._userId) {
|
|
4231
|
+
// Only generate a new userId if we don't already have one
|
|
4232
|
+
// (preserves userId loaded from localStorage or received from server)
|
|
4233
|
+
this._userId = "mcp-widget-user-".concat(uuid_1());
|
|
4234
|
+
}
|
|
4235
|
+
// Session ID priority:
|
|
4236
|
+
// 1. If explicitly provided via setVisitorInfo, use it
|
|
4237
|
+
// 2. If empty string provided, generate a new session ID
|
|
4238
|
+
// 3. If not provided (undefined) and we have an existing session, keep it
|
|
4239
|
+
if (sessionId) {
|
|
4240
|
+
// Explicit session ID provided - use it
|
|
4241
|
+
this._sessionId = sessionId;
|
|
4242
|
+
log("Using provided session id: ".concat(this._sessionId));
|
|
4243
|
+
}
|
|
4244
|
+
else if (sessionId === "") {
|
|
4245
|
+
// Empty string explicitly passed - generate new session
|
|
4246
|
+
this._sessionId = "mcp-widget-session-".concat(uuid_1());
|
|
4247
|
+
this.dispatch(setSessionId(this._sessionId));
|
|
4248
|
+
log("Using generated session id: ".concat(this._sessionId));
|
|
4249
|
+
}
|
|
4250
|
+
else if (!this._sessionId && this.config.sessionId) {
|
|
4251
|
+
// No existing session and config has one - use config
|
|
4252
|
+
this._sessionId = this.config.sessionId;
|
|
4253
|
+
log("Using config session id: ".concat(this._sessionId));
|
|
4254
|
+
}
|
|
4255
|
+
else if (!this._sessionId) {
|
|
4256
|
+
// No session ID at all - generate one
|
|
4257
|
+
this._sessionId = "mcp-widget-session-".concat(uuid_1());
|
|
4258
|
+
this.dispatch(setSessionId(this._sessionId));
|
|
4259
|
+
log("Using generated session id: ".concat(this._sessionId));
|
|
4260
|
+
}
|
|
4261
|
+
else {
|
|
4262
|
+
log("Keeping existing session id: ".concat(this._sessionId));
|
|
4263
|
+
}
|
|
4264
|
+
this.isNewSession = true;
|
|
4265
|
+
};
|
|
4266
|
+
Object.defineProperty(MCPChat.prototype, "userId", {
|
|
4267
|
+
get: function () {
|
|
4268
|
+
return this._userId;
|
|
4269
|
+
},
|
|
4270
|
+
enumerable: false,
|
|
4271
|
+
configurable: true
|
|
4272
|
+
});
|
|
4273
|
+
Object.defineProperty(MCPChat.prototype, "sessionId", {
|
|
4274
|
+
get: function () {
|
|
4275
|
+
return this._sessionId;
|
|
4276
|
+
},
|
|
4277
|
+
enumerable: false,
|
|
4278
|
+
configurable: true
|
|
4279
|
+
});
|
|
4280
|
+
return MCPChat;
|
|
4281
|
+
}());
|
|
4282
|
+
|
|
4283
|
+
/**
|
|
4284
|
+
* Sends a POST to your STENTOR based server.
|
|
4285
|
+
*
|
|
4286
|
+
* @param data
|
|
4287
|
+
* @param url
|
|
4288
|
+
* @param key
|
|
4289
|
+
* @param signal
|
|
4290
|
+
* @returns
|
|
4291
|
+
*/
|
|
4292
|
+
function postMessageToStentor(data, url, key, signal) {
|
|
4293
|
+
return __awaiter$1(this, void 0, void 0, function () {
|
|
4294
|
+
var body, response;
|
|
4295
|
+
return __generator$1(this, function (_a) {
|
|
4296
|
+
switch (_a.label) {
|
|
4297
|
+
case 0:
|
|
4298
|
+
body = JSON.stringify(data);
|
|
4299
|
+
log("URL: ".concat(url));
|
|
4300
|
+
log("BODY: ".concat(body));
|
|
4301
|
+
return [4 /*yield*/, fetch(url, {
|
|
4302
|
+
method: "POST",
|
|
4303
|
+
headers: {
|
|
4304
|
+
"Content-Type": "application/json",
|
|
4305
|
+
"Authorization": "Bearer ".concat(key),
|
|
4306
|
+
},
|
|
4307
|
+
body: body,
|
|
4308
|
+
mode: "cors",
|
|
4309
|
+
signal: signal
|
|
4310
|
+
})];
|
|
4311
|
+
case 1:
|
|
4312
|
+
response = _a.sent();
|
|
4313
|
+
if (!response.ok) {
|
|
4314
|
+
throw new Error("Status ".concat(response.status, ", Text: ").concat(response.statusText));
|
|
4315
|
+
}
|
|
4316
|
+
return [2 /*return*/, response.json()];
|
|
4317
|
+
}
|
|
4318
|
+
});
|
|
4319
|
+
});
|
|
4320
|
+
}
|
|
4321
|
+
|
|
4322
|
+
function convertFromListDisplay(list) {
|
|
4323
|
+
return {
|
|
4324
|
+
type: list.type,
|
|
4325
|
+
title: list.title,
|
|
4326
|
+
items: list.items.map(function (item) {
|
|
4327
|
+
var _a, _b;
|
|
4328
|
+
var responseItem = {
|
|
4329
|
+
title: item.title,
|
|
4330
|
+
subTitle: item.description,
|
|
4331
|
+
token: item.token,
|
|
4332
|
+
url: item.url,
|
|
4333
|
+
hideUrl: item.hideUrl,
|
|
4334
|
+
imageUrl: (_a = item.image) === null || _a === void 0 ? void 0 : _a.url,
|
|
4335
|
+
imageActionUrl: (_b = item.image) === null || _b === void 0 ? void 0 : _b.imageActionUrl
|
|
4336
|
+
};
|
|
4337
|
+
// TODO: Remove any when the buttons are defined on the list (ListButton - title + openUrlAction)
|
|
4338
|
+
var itemButtons = item.buttons;
|
|
4339
|
+
if (itemButtons && itemButtons.length > 0) {
|
|
4340
|
+
responseItem.buttons = itemButtons.map(function (button) { return ({
|
|
4341
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4342
|
+
actionUrl: button.openUrlAction,
|
|
4343
|
+
label: button.title
|
|
4344
|
+
}); });
|
|
4345
|
+
}
|
|
4346
|
+
return responseItem;
|
|
4347
|
+
})
|
|
4348
|
+
};
|
|
4349
|
+
}
|
|
4350
|
+
function convertToListDisplay(list) {
|
|
4351
|
+
return {
|
|
4352
|
+
type: list.type,
|
|
4353
|
+
title: list.title,
|
|
4354
|
+
items: (list.items || []).map(function (item) {
|
|
4355
|
+
var responseItem = {
|
|
4356
|
+
title: item.title,
|
|
4357
|
+
description: item.subTitle,
|
|
4358
|
+
token: item.token,
|
|
4359
|
+
url: item.url,
|
|
4360
|
+
hideUrl: item.hideUrl,
|
|
4361
|
+
image: {
|
|
4362
|
+
url: item.imageUrl,
|
|
4363
|
+
imageActionUrl: item.imageActionUrl,
|
|
4364
|
+
accessibilityText: ""
|
|
4365
|
+
}
|
|
4366
|
+
};
|
|
4367
|
+
// TODO: Remove any when the buttons are defined on the list (ListButton - title + openUrlAction)
|
|
4368
|
+
var itemButtons = item.buttons;
|
|
4369
|
+
if (itemButtons && itemButtons.length > 0) {
|
|
4370
|
+
responseItem.buttons = itemButtons.map(function (button) { return ({
|
|
4371
|
+
openUrlAction: button.actionUrl,
|
|
4372
|
+
title: button.label
|
|
4373
|
+
}); });
|
|
4374
|
+
}
|
|
4375
|
+
return responseItem;
|
|
4376
|
+
})
|
|
4377
|
+
};
|
|
4378
|
+
}
|
|
4379
|
+
function convertFromCardDisplay(card) {
|
|
4380
|
+
return {
|
|
4381
|
+
content: card.content,
|
|
4382
|
+
imageUrl: card.smallImageUrl,
|
|
4383
|
+
title: card.title,
|
|
4384
|
+
imageActionUrl: card === null || card === void 0 ? void 0 : card.imageActionUrl,
|
|
4385
|
+
buttons: card.buttons ? card.buttons.map(function (button) { return ({
|
|
4386
|
+
actionUrl: button.openUrlAction,
|
|
4387
|
+
label: button.title
|
|
4388
|
+
}); }) : undefined
|
|
4389
|
+
};
|
|
4390
|
+
}
|
|
4391
|
+
function getOutput(value) {
|
|
4392
|
+
if (value) {
|
|
4393
|
+
if (typeof value === "string") {
|
|
4394
|
+
return {
|
|
4395
|
+
displayText: value
|
|
4396
|
+
};
|
|
4397
|
+
}
|
|
4398
|
+
else {
|
|
4399
|
+
return value;
|
|
4400
|
+
}
|
|
4401
|
+
}
|
|
4402
|
+
return undefined;
|
|
4403
|
+
}
|
|
4404
|
+
/**
|
|
4405
|
+
* Converts a Stentor Response to a ChatMessageRequest
|
|
4406
|
+
*
|
|
4407
|
+
* @param botResponse
|
|
4408
|
+
* @param now
|
|
4409
|
+
* @returns
|
|
4410
|
+
*/
|
|
4411
|
+
function responseToMessage(botResponse, now) {
|
|
4412
|
+
var _a, _b, _c, _d, _e;
|
|
4413
|
+
if (now === void 0) { now = new Date().getTime(); }
|
|
4414
|
+
var responseMessage;
|
|
4415
|
+
if (!botResponse) {
|
|
4416
|
+
return responseMessage;
|
|
4417
|
+
}
|
|
4418
|
+
var text;
|
|
4419
|
+
var html;
|
|
4420
|
+
var endSession;
|
|
4421
|
+
var outputSpeech = getOutput(botResponse.outputSpeech);
|
|
4422
|
+
var reprompt = getOutput(botResponse.reprompt);
|
|
4423
|
+
if (outputSpeech) {
|
|
4424
|
+
text = outputSpeech.displayText;
|
|
4425
|
+
html = outputSpeech.html;
|
|
4426
|
+
}
|
|
4427
|
+
if (botResponse.system === "TRANSFER_CALL") {
|
|
4428
|
+
responseMessage = {
|
|
4429
|
+
type: "handOff",
|
|
4430
|
+
timestamp: now,
|
|
4431
|
+
handoffMessage: text,
|
|
4432
|
+
handoffTarget: (_a = botResponse === null || botResponse === void 0 ? void 0 : botResponse.data) === null || _a === void 0 ? void 0 : _a.transferPhoneNumber,
|
|
4433
|
+
endSession: false,
|
|
4434
|
+
user: undefined
|
|
4435
|
+
};
|
|
4436
|
+
}
|
|
4437
|
+
else if ((_b = botResponse.system) === null || _b === void 0 ? void 0 : _b.startsWith("PERMISSION_")) {
|
|
4438
|
+
responseMessage = getPermissionResponse(botResponse, now);
|
|
4439
|
+
}
|
|
4440
|
+
else {
|
|
4441
|
+
endSession = !reprompt || !(reprompt.displayText || reprompt.ssml);
|
|
4442
|
+
responseMessage = {
|
|
4443
|
+
type: "msg",
|
|
4444
|
+
timestamp: now,
|
|
4445
|
+
msg: {
|
|
4446
|
+
displays: botResponse.displays,
|
|
4447
|
+
context: (_d = (_c = botResponse.context) === null || _c === void 0 ? void 0 : _c.active) === null || _d === void 0 ? void 0 : _d.map(function (ctx) { return ctx.name; })
|
|
4448
|
+
},
|
|
4449
|
+
endSession: endSession,
|
|
4450
|
+
user: undefined
|
|
4451
|
+
};
|
|
4452
|
+
if (text && !html) {
|
|
4453
|
+
responseMessage.msg = __assign(__assign({}, responseMessage.msg), { text: text });
|
|
4454
|
+
}
|
|
4455
|
+
if (html) {
|
|
4456
|
+
responseMessage.msg = __assign(__assign({}, responseMessage.msg), { html: html });
|
|
4457
|
+
}
|
|
4458
|
+
if ((_e = outputSpeech === null || outputSpeech === void 0 ? void 0 : outputSpeech.suggestions) === null || _e === void 0 ? void 0 : _e.length) {
|
|
4459
|
+
responseMessage.msg.options = outputSpeech.suggestions.map(function (suggestion) {
|
|
4460
|
+
if (typeof suggestion === "string") {
|
|
4461
|
+
// Simple chips (strings)
|
|
4462
|
+
return suggestion;
|
|
4463
|
+
}
|
|
4464
|
+
else {
|
|
4465
|
+
// "call out" chips
|
|
4466
|
+
return {
|
|
4467
|
+
label: suggestion.title,
|
|
4468
|
+
actionUrl: suggestion.url
|
|
4469
|
+
};
|
|
4470
|
+
}
|
|
4471
|
+
});
|
|
4472
|
+
}
|
|
4473
|
+
}
|
|
4474
|
+
return responseMessage;
|
|
4475
|
+
}
|
|
4476
|
+
function getPermissionRequestType(type) {
|
|
4477
|
+
switch (type) {
|
|
4478
|
+
case "PERMISSION_EMAIL":
|
|
4479
|
+
return "EMAIL";
|
|
4480
|
+
case "PERMISSION_LOCATION_PRECISE":
|
|
4481
|
+
return "LOCATION_PRECISE";
|
|
4482
|
+
default:
|
|
4483
|
+
throw new Error("Unsupported permission: ".concat(type));
|
|
4484
|
+
}
|
|
4485
|
+
}
|
|
4486
|
+
function getPermissionResponse(botResponse, now) {
|
|
4487
|
+
var _a;
|
|
4488
|
+
var type = getPermissionRequestType(botResponse.system);
|
|
4489
|
+
var outputSpeech = getOutput(botResponse.outputSpeech);
|
|
4490
|
+
var permissionPrimerAccepted = botResponse.data.permissionPrimerAccepted;
|
|
4491
|
+
var permissionDenied = botResponse.data.permissionDenied;
|
|
4492
|
+
return {
|
|
4493
|
+
type: "permissionRequest",
|
|
4494
|
+
timestamp: now,
|
|
4495
|
+
msg: {
|
|
4496
|
+
text: (_a = outputSpeech === null || outputSpeech === void 0 ? void 0 : outputSpeech.displayText) !== null && _a !== void 0 ? _a : "".concat(botResponse.data.permissionRequestTTSContext),
|
|
4497
|
+
permissionRequest: {
|
|
4498
|
+
time: now,
|
|
4499
|
+
type: type,
|
|
4500
|
+
approve: typeof permissionPrimerAccepted === "object" ? responseToMessage({
|
|
4501
|
+
outputSpeech: permissionPrimerAccepted
|
|
4502
|
+
}).msg : undefined,
|
|
4503
|
+
deny: typeof permissionDenied === "object" ? responseToMessage({
|
|
4504
|
+
outputSpeech: permissionDenied
|
|
4505
|
+
}).msg : undefined
|
|
4506
|
+
}
|
|
4507
|
+
},
|
|
4508
|
+
endSession: false,
|
|
4509
|
+
user: undefined //todo: set
|
|
4510
|
+
};
|
|
4511
|
+
}
|
|
4512
|
+
|
|
2882
4513
|
var PERMISSION_QUESTION_EXPIRATION_MS$1 = 300000; // 5 minutes
|
|
2883
4514
|
// interface UserLeaveMessage {
|
|
2884
4515
|
// readonly user: ChatUserInfo;
|
|
@@ -4761,7 +6392,7 @@ function createPacketDecoderStream(maxPayload, binaryType) {
|
|
|
4761
6392
|
},
|
|
4762
6393
|
});
|
|
4763
6394
|
}
|
|
4764
|
-
const protocol
|
|
6395
|
+
const protocol = 4;
|
|
4765
6396
|
|
|
4766
6397
|
/**
|
|
4767
6398
|
* Expose `Emitter`.
|
|
@@ -6037,7 +7668,7 @@ class SocketWithoutUpgrade extends Emitter_1 {
|
|
|
6037
7668
|
createTransport(name) {
|
|
6038
7669
|
const query = Object.assign({}, this.opts.query);
|
|
6039
7670
|
// append engine.io protocol identifier
|
|
6040
|
-
query.EIO = protocol
|
|
7671
|
+
query.EIO = protocol;
|
|
6041
7672
|
// transport name
|
|
6042
7673
|
query.transport = name;
|
|
6043
7674
|
// session id if we already have one
|
|
@@ -6412,7 +8043,7 @@ class SocketWithoutUpgrade extends Emitter_1 {
|
|
|
6412
8043
|
}
|
|
6413
8044
|
}
|
|
6414
8045
|
}
|
|
6415
|
-
SocketWithoutUpgrade.protocol = protocol
|
|
8046
|
+
SocketWithoutUpgrade.protocol = protocol;
|
|
6416
8047
|
/**
|
|
6417
8048
|
* This class provides a WebSocket-like interface to connect to an Engine.IO server. The connection will be established
|
|
6418
8049
|
* with one of the available low-level transports, like HTTP long-polling, WebSocket or WebTransport.
|
|
@@ -6808,12 +8439,6 @@ const RESERVED_EVENTS$1 = [
|
|
|
6808
8439
|
"newListener",
|
|
6809
8440
|
"removeListener", // used by the Node.js EventEmitter
|
|
6810
8441
|
];
|
|
6811
|
-
/**
|
|
6812
|
-
* Protocol version.
|
|
6813
|
-
*
|
|
6814
|
-
* @public
|
|
6815
|
-
*/
|
|
6816
|
-
const protocol = 5;
|
|
6817
8442
|
var PacketType;
|
|
6818
8443
|
(function (PacketType) {
|
|
6819
8444
|
PacketType[PacketType["CONNECT"] = 0] = "CONNECT";
|
|
@@ -7110,8 +8735,7 @@ var parser = /*#__PURE__*/Object.freeze({
|
|
|
7110
8735
|
__proto__: null,
|
|
7111
8736
|
Decoder: Decoder,
|
|
7112
8737
|
Encoder: Encoder,
|
|
7113
|
-
get PacketType () { return PacketType; }
|
|
7114
|
-
protocol: protocol
|
|
8738
|
+
get PacketType () { return PacketType; }
|
|
7115
8739
|
});
|
|
7116
8740
|
|
|
7117
8741
|
function on(obj, ev, fn) {
|
|
@@ -8665,6 +10289,7 @@ var StentorServerChat = /** @class */ (function () {
|
|
|
8665
10289
|
}());
|
|
8666
10290
|
|
|
8667
10291
|
function createChatServerCore(config, options) {
|
|
10292
|
+
var _a, _b, _c, _d, _e;
|
|
8668
10293
|
switch (config.type) {
|
|
8669
10294
|
case "direct":
|
|
8670
10295
|
return new StentorDirectChat({
|
|
@@ -8680,6 +10305,15 @@ function createChatServerCore(config, options) {
|
|
|
8680
10305
|
return new StentorRouterChat({
|
|
8681
10306
|
url: config.serverUrl,
|
|
8682
10307
|
}, options);
|
|
10308
|
+
case "mcp":
|
|
10309
|
+
return new MCPChat({
|
|
10310
|
+
serverUrl: config.serverUrl,
|
|
10311
|
+
mode: (_a = config.mcp) === null || _a === void 0 ? void 0 : _a.mode,
|
|
10312
|
+
authToken: (_b = config.mcp) === null || _b === void 0 ? void 0 : _b.authToken,
|
|
10313
|
+
debugMode: (_c = config.mcp) === null || _c === void 0 ? void 0 : _c.debugMode,
|
|
10314
|
+
pageContextOverride: (_d = config.mcp) === null || _d === void 0 ? void 0 : _d.pageContextOverride,
|
|
10315
|
+
showAiBadge: (_e = config.mcp) === null || _e === void 0 ? void 0 : _e.showAiBadge,
|
|
10316
|
+
}, options);
|
|
8683
10317
|
case "local":
|
|
8684
10318
|
return new StentorLocalChat();
|
|
8685
10319
|
default:
|
|
@@ -8833,6 +10467,79 @@ var DrawerBars = function (props) {
|
|
|
8833
10467
|
return (require$$0.jsx("button", { className: "drawer-bars", tabIndex: props.tabIndex ? Number(props.tabIndex) : 0, "aria-label": "open menu", "aria-hidden": false, onClick: props.onToggle, children: getBars() }));
|
|
8834
10468
|
};
|
|
8835
10469
|
|
|
10470
|
+
/**
|
|
10471
|
+
* Type guard for ChatMsgDetail.
|
|
10472
|
+
* Use this to safely narrow ChatDetail to ChatMsgDetail.
|
|
10473
|
+
*/
|
|
10474
|
+
function isChatMsgDetail(chat) {
|
|
10475
|
+
return chat.type === "chat.msg";
|
|
10476
|
+
}
|
|
10477
|
+
|
|
10478
|
+
/**
|
|
10479
|
+
* Formats a suggestion chip for export
|
|
10480
|
+
*/
|
|
10481
|
+
function formatSuggestion(option) {
|
|
10482
|
+
if (isChatServerActionLink(option)) {
|
|
10483
|
+
return option.actionUrl ? "[".concat(option.label, "](").concat(option.actionUrl, ")") : option.label;
|
|
10484
|
+
}
|
|
10485
|
+
return option;
|
|
10486
|
+
}
|
|
10487
|
+
/**
|
|
10488
|
+
* Formats chat messages for export
|
|
10489
|
+
*/
|
|
10490
|
+
function formatConversation(chats) {
|
|
10491
|
+
return chats
|
|
10492
|
+
.filter(isChatMsgDetail)
|
|
10493
|
+
.map(function (chat) {
|
|
10494
|
+
var _a, _b, _c;
|
|
10495
|
+
var isVisitor = (_a = chat.user.nick) === null || _a === void 0 ? void 0 : _a.startsWith("visitor:");
|
|
10496
|
+
var role = isVisitor ? "User" : "Assistant";
|
|
10497
|
+
var text = ((_b = chat.msg) === null || _b === void 0 ? void 0 : _b.text) || "";
|
|
10498
|
+
var result = "".concat(role, ": ").concat(text);
|
|
10499
|
+
// Add suggestion chips if present
|
|
10500
|
+
var options = (_c = chat.msg) === null || _c === void 0 ? void 0 : _c.options;
|
|
10501
|
+
if (options && options.length > 0) {
|
|
10502
|
+
var suggestions = options.map(formatSuggestion).join(", ");
|
|
10503
|
+
result += "\nSuggestions: ".concat(suggestions);
|
|
10504
|
+
}
|
|
10505
|
+
return result;
|
|
10506
|
+
})
|
|
10507
|
+
.join("\n");
|
|
10508
|
+
}
|
|
10509
|
+
var ExportButton = function (props) {
|
|
10510
|
+
var chats = reactRedux.useSelector(function (state) { return state.chats; });
|
|
10511
|
+
var handleClick = function () { return __awaiter$1(void 0, void 0, void 0, function () {
|
|
10512
|
+
var conversation, textarea;
|
|
10513
|
+
return __generator$1(this, function (_b) {
|
|
10514
|
+
switch (_b.label) {
|
|
10515
|
+
case 0:
|
|
10516
|
+
conversation = formatConversation(chats);
|
|
10517
|
+
_b.label = 1;
|
|
10518
|
+
case 1:
|
|
10519
|
+
_b.trys.push([1, 3, , 4]);
|
|
10520
|
+
return [4 /*yield*/, navigator.clipboard.writeText(conversation)];
|
|
10521
|
+
case 2:
|
|
10522
|
+
_b.sent();
|
|
10523
|
+
return [3 /*break*/, 4];
|
|
10524
|
+
case 3:
|
|
10525
|
+
_b.sent();
|
|
10526
|
+
// Fallback for older browsers using deprecated execCommand
|
|
10527
|
+
// Note: execCommand is deprecated and may be removed in future browsers
|
|
10528
|
+
console.warn("[ExportButton] Using deprecated execCommand fallback for clipboard copy");
|
|
10529
|
+
textarea = document.createElement("textarea");
|
|
10530
|
+
textarea.value = conversation;
|
|
10531
|
+
document.body.appendChild(textarea);
|
|
10532
|
+
textarea.select();
|
|
10533
|
+
document.execCommand("copy");
|
|
10534
|
+
document.body.removeChild(textarea);
|
|
10535
|
+
return [3 /*break*/, 4];
|
|
10536
|
+
case 4: return [2 /*return*/];
|
|
10537
|
+
}
|
|
10538
|
+
});
|
|
10539
|
+
}); };
|
|
10540
|
+
return (require$$0.jsx("div", { id: "xapp-widget-export", "aria-label": "export conversation", "aria-hidden": false, tabIndex: props.tabIndex ? Number(props.tabIndex) : 0, className: "export-button", onClick: handleClick }));
|
|
10541
|
+
};
|
|
10542
|
+
|
|
8836
10543
|
var MenuButton = function (props) {
|
|
8837
10544
|
return (require$$0.jsx("div", { id: "xapp-widget-menu", "aria-label": "open menu", "aria-hidden": false, tabIndex: props.tabIndex ? Number(props.tabIndex) : 0, className: "menu-button", onClick: props.onClick }));
|
|
8838
10545
|
};
|
|
@@ -8976,7 +10683,7 @@ var closeButtonAriaLabel = "To close widget click on close icon in top right sid
|
|
|
8976
10683
|
var ChatHeader = function (props) {
|
|
8977
10684
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
|
|
8978
10685
|
var innerDispatch = useChatDispatch();
|
|
8979
|
-
var menuConfig = props.menuConfig, onSubmit = props.onSubmit;
|
|
10686
|
+
var menuConfig = props.menuConfig, onSubmit = props.onSubmit, debugMode = props.debugMode;
|
|
8980
10687
|
var _r = require$$1.useState(false), drawerOpen = _r[0], setDrawerState = _r[1]; // false initially
|
|
8981
10688
|
var menuPosition = (menuConfig === null || menuConfig === void 0 ? void 0 : menuConfig.menuButtonLocation) || "FOOTER";
|
|
8982
10689
|
var showMenuLeft = menuPosition === "HEADER_LEFT";
|
|
@@ -9015,7 +10722,7 @@ var ChatHeader = function (props) {
|
|
|
9015
10722
|
? "status-text-positionLeftNoAvatar"
|
|
9016
10723
|
: "status-text-positionLeft", "\n ").concat(((_d = props.config) === null || _d === void 0 ? void 0 : _d.alignTextCenter)
|
|
9017
10724
|
? "status-text-positionCenter"
|
|
9018
|
-
: "", " \n "), children: [require$$0.jsx("span", { className: "status-text-title", children: getStatusText(props.accountStatus, (_e = props.config) === null || _e === void 0 ? void 0 : _e.status) }), ((_g = (_f = props.config) === null || _f === void 0 ? void 0 : _f.subtitle) === null || _g === void 0 ? void 0 : _g.enabled) && (require$$0.jsx("span", { className: "status-text-subtitle", children: renderSubtitleText((_j = (_h = props.config) === null || _h === void 0 ? void 0 : _h.subtitle) === null || _j === void 0 ? void 0 : _j.text) }))] }), require$$0.jsxs(ButtonGroup, { children: [hasRightMenu && (require$$0.jsx(MenuButton, { onClick: toggleDrawer, tabIndex: menuButtonTabIndex })), props.canRefresh && (require$$0.jsx(RefreshButton, { onClick: props.refreshOnClick, tabIndex: (_l = (_k = props.config) === null || _k === void 0 ? void 0 : _k.actions) === null || _l === void 0 ? void 0 : _l.refreshTabIndex, showInLeft: false, showInRight: false })), props.canMinimize && (require$$0.jsx(MinimizeButton, { onClick: props.minimizeOnClick, tabIndex: (_o = (_m = props.config) === null || _m === void 0 ? void 0 : _m.actions) === null || _o === void 0 ? void 0 : _o.minimizeTabIndex, showInRight: false })), props.canCancel && (require$$0.jsx(CancelButton, { onClick: props.cancelOnClick, tabIndex: (_q = (_p = props.config) === null || _p === void 0 ? void 0 : _p.actions) === null || _q === void 0 ? void 0 : _q.cancelTabIndex }))] })] }), drawerOpen ? (require$$0.jsx("div", { className: "xa-chat-menu-container", children: require$$0.jsx(ChatMenu, { openFrom: showMenuRight ? "right" : "left", opened: drawerOpen, tabIndex: menuItemsTabIndex, onItemClick: handleMenuItem, items: menuItems }) })) : (require$$0.jsx(require$$0.Fragment, {}))] }));
|
|
10725
|
+
: "", " \n "), children: [require$$0.jsx("span", { className: "status-text-title", children: getStatusText(props.accountStatus, (_e = props.config) === null || _e === void 0 ? void 0 : _e.status) }), ((_g = (_f = props.config) === null || _f === void 0 ? void 0 : _f.subtitle) === null || _g === void 0 ? void 0 : _g.enabled) && (require$$0.jsx("span", { className: "status-text-subtitle", children: renderSubtitleText((_j = (_h = props.config) === null || _h === void 0 ? void 0 : _h.subtitle) === null || _j === void 0 ? void 0 : _j.text) }))] }), require$$0.jsxs(ButtonGroup, { children: [hasRightMenu && (require$$0.jsx(MenuButton, { onClick: toggleDrawer, tabIndex: menuButtonTabIndex })), debugMode && require$$0.jsx(ExportButton, {}), props.canRefresh && (require$$0.jsx(RefreshButton, { onClick: props.refreshOnClick, tabIndex: (_l = (_k = props.config) === null || _k === void 0 ? void 0 : _k.actions) === null || _l === void 0 ? void 0 : _l.refreshTabIndex, showInLeft: false, showInRight: false })), props.canMinimize && (require$$0.jsx(MinimizeButton, { onClick: props.minimizeOnClick, tabIndex: (_o = (_m = props.config) === null || _m === void 0 ? void 0 : _m.actions) === null || _o === void 0 ? void 0 : _o.minimizeTabIndex, showInRight: false })), props.canCancel && (require$$0.jsx(CancelButton, { onClick: props.cancelOnClick, tabIndex: (_q = (_p = props.config) === null || _p === void 0 ? void 0 : _p.actions) === null || _q === void 0 ? void 0 : _q.cancelTabIndex }))] })] }), drawerOpen ? (require$$0.jsx("div", { className: "xa-chat-menu-container", children: require$$0.jsx(ChatMenu, { openFrom: showMenuRight ? "right" : "left", opened: drawerOpen, tabIndex: menuItemsTabIndex, onItemClick: handleMenuItem, items: menuItems }) })) : (require$$0.jsx(require$$0.Fragment, {}))] }));
|
|
9019
10726
|
};
|
|
9020
10727
|
|
|
9021
10728
|
var UnknownMessage = function () { return require$$0.jsx(require$$0.Fragment, {}); };
|
|
@@ -9029,7 +10736,8 @@ var ChatMessagePart = function (props) {
|
|
|
9029
10736
|
var containerClass = "xappw-chat-msg-part" +
|
|
9030
10737
|
(position === "below" ? " xappw-chat-msg-part--avatar-below" : "") +
|
|
9031
10738
|
(position === "bottom" ? " xappw-chat-msg-part--avatar-bottom" : "") +
|
|
9032
|
-
(props.fullWidth ? " xappw-chat-msg-part--full-width" : "")
|
|
10739
|
+
(props.fullWidth ? " xappw-chat-msg-part--full-width" : "") +
|
|
10740
|
+
(props.isStreaming ? " xappw-chat-msg-part--streaming" : "");
|
|
9033
10741
|
var user = props.user, hideUserInfo = props.hideUserInfo;
|
|
9034
10742
|
// Hide user info if hideUserInfo is true and position is "bottom"
|
|
9035
10743
|
var shouldHideUserInfo = hideUserInfo && position === "bottom";
|
|
@@ -9527,12 +11235,169 @@ var ChatPermissionMessage = function (props) {
|
|
|
9527
11235
|
return (require$$0.jsxs("div", { className: "chat-msg", children: [require$$0.jsx("span", { className: "message-sr-only", children: "at " + props.time + (agentMessage ? " the bot said" : " the user said") }), require$$0.jsxs("div", { className: "buttons-container", children: [require$$0.jsx(ActionButton, { label: allowLabel, addClass: "button", onClick: handleAllow }), require$$0.jsx(ActionButton, { label: denyLabel, addClass: "button", onClick: handleDeny })] })] }));
|
|
9528
11236
|
};
|
|
9529
11237
|
|
|
11238
|
+
var CopyButton = function (_a) {
|
|
11239
|
+
var text = _a.text;
|
|
11240
|
+
var _b = require$$1.useState(false), copied = _b[0], setCopied = _b[1];
|
|
11241
|
+
var handleCopy = function (e) { return __awaiter$1(void 0, void 0, void 0, function () {
|
|
11242
|
+
var textarea;
|
|
11243
|
+
return __generator$1(this, function (_b) {
|
|
11244
|
+
switch (_b.label) {
|
|
11245
|
+
case 0:
|
|
11246
|
+
e.stopPropagation();
|
|
11247
|
+
_b.label = 1;
|
|
11248
|
+
case 1:
|
|
11249
|
+
_b.trys.push([1, 3, , 4]);
|
|
11250
|
+
return [4 /*yield*/, navigator.clipboard.writeText(text)];
|
|
11251
|
+
case 2:
|
|
11252
|
+
_b.sent();
|
|
11253
|
+
setCopied(true);
|
|
11254
|
+
setTimeout(function () { return setCopied(false); }, 1500);
|
|
11255
|
+
return [3 /*break*/, 4];
|
|
11256
|
+
case 3:
|
|
11257
|
+
_b.sent();
|
|
11258
|
+
textarea = document.createElement("textarea");
|
|
11259
|
+
textarea.value = text;
|
|
11260
|
+
document.body.appendChild(textarea);
|
|
11261
|
+
textarea.select();
|
|
11262
|
+
document.execCommand("copy");
|
|
11263
|
+
document.body.removeChild(textarea);
|
|
11264
|
+
setCopied(true);
|
|
11265
|
+
setTimeout(function () { return setCopied(false); }, 1500);
|
|
11266
|
+
return [3 /*break*/, 4];
|
|
11267
|
+
case 4: return [2 /*return*/];
|
|
11268
|
+
}
|
|
11269
|
+
});
|
|
11270
|
+
}); };
|
|
11271
|
+
return (require$$0.jsx("button", { className: "tool-call-item__copy-btn", onClick: handleCopy, title: "Copy to clipboard", children: copied ? "✓" : "⧉" }));
|
|
11272
|
+
};
|
|
11273
|
+
/**
|
|
11274
|
+
* Gets the display text for a tool call, using fallback chain:
|
|
11275
|
+
* label -> displayName -> toolName -> "Working..."
|
|
11276
|
+
*/
|
|
11277
|
+
var getDisplayLabel = function (toolCall) {
|
|
11278
|
+
var _a, _b, _c;
|
|
11279
|
+
return (_c = (_b = (_a = toolCall.label) !== null && _a !== void 0 ? _a : toolCall.displayName) !== null && _b !== void 0 ? _b : toolCall.name) !== null && _c !== void 0 ? _c : "Working...";
|
|
11280
|
+
};
|
|
11281
|
+
/**
|
|
11282
|
+
* User-friendly tool progress item (non-debug mode)
|
|
11283
|
+
* Shows just the label with a spinner or checkmark
|
|
11284
|
+
*/
|
|
11285
|
+
var ToolProgressItem = function (_a) {
|
|
11286
|
+
var toolCall = _a.toolCall;
|
|
11287
|
+
var label = getDisplayLabel(toolCall);
|
|
11288
|
+
return (require$$0.jsxs("div", { className: "tool-progress-item ".concat(toolCall.isExecuting ? "tool-progress-item--executing" : "", " ").concat(toolCall.error ? "tool-progress-item--error" : ""), children: [require$$0.jsx("span", { className: "tool-progress-item__icon", children: toolCall.isExecuting ? (require$$0.jsx("span", { className: "tool-progress-item__spinner", "aria-label": "executing" })) : toolCall.error ? (require$$0.jsx("span", { className: "tool-progress-item__status tool-progress-item__status--error", children: "!" })) : (require$$0.jsx("span", { className: "tool-progress-item__status tool-progress-item__status--success", children: "\u2713" })) }), require$$0.jsx("span", { className: "tool-progress-item__label", children: label })] }));
|
|
11289
|
+
};
|
|
11290
|
+
/**
|
|
11291
|
+
* Debug mode tool call item with expandable details
|
|
11292
|
+
* Shows label, tool name, and expandable input/output
|
|
11293
|
+
*/
|
|
11294
|
+
var ToolCallItem = function (_a) {
|
|
11295
|
+
var toolCall = _a.toolCall;
|
|
11296
|
+
var _b = require$$1.useState(false), isExpanded = _b[0], setIsExpanded = _b[1];
|
|
11297
|
+
var toggleExpanded = function () { return setIsExpanded(!isExpanded); };
|
|
11298
|
+
var formatJson = function (data) {
|
|
11299
|
+
if (data === undefined || data === null)
|
|
11300
|
+
return "";
|
|
11301
|
+
try {
|
|
11302
|
+
// If it's a string, try to parse it as JSON first (server may send stringified JSON)
|
|
11303
|
+
if (typeof data === "string") {
|
|
11304
|
+
try {
|
|
11305
|
+
var parsed = JSON.parse(data);
|
|
11306
|
+
return JSON.stringify(parsed, null, 2);
|
|
11307
|
+
}
|
|
11308
|
+
catch (_a) {
|
|
11309
|
+
// Not valid JSON, return as-is
|
|
11310
|
+
return data;
|
|
11311
|
+
}
|
|
11312
|
+
}
|
|
11313
|
+
return JSON.stringify(data, null, 2);
|
|
11314
|
+
}
|
|
11315
|
+
catch (_b) {
|
|
11316
|
+
return String(data);
|
|
11317
|
+
}
|
|
11318
|
+
};
|
|
11319
|
+
var label = getDisplayLabel(toolCall);
|
|
11320
|
+
var duration = toolCall.endTime
|
|
11321
|
+
? "".concat(((toolCall.endTime - toolCall.startTime) / 1000).toFixed(2), "s")
|
|
11322
|
+
: null;
|
|
11323
|
+
var hasInput = toolCall.input !== undefined && toolCall.input !== null;
|
|
11324
|
+
var hasResult = toolCall.result !== undefined && toolCall.result !== null;
|
|
11325
|
+
var hasError = !!toolCall.error;
|
|
11326
|
+
return (require$$0.jsxs("div", { className: "tool-call-item ".concat(toolCall.isExecuting ? "tool-call-item--executing" : ""), children: [require$$0.jsxs("button", { className: "tool-call-item__header", onClick: toggleExpanded, "aria-expanded": isExpanded, children: [require$$0.jsx("span", { className: "tool-call-item__icon", children: toolCall.isExecuting ? (require$$0.jsx("span", { className: "tool-call-item__spinner", "aria-label": "executing" })) : toolCall.error ? (require$$0.jsx("span", { className: "tool-call-item__status tool-call-item__status--error", children: "!" })) : (require$$0.jsx("span", { className: "tool-call-item__status tool-call-item__status--success", children: "\u2713" })) }), require$$0.jsxs("span", { className: "tool-call-item__info", children: [require$$0.jsx("span", { className: "tool-call-item__label", children: label }), require$$0.jsx("span", { className: "tool-call-item__name", children: toolCall.name })] }), duration && require$$0.jsx("span", { className: "tool-call-item__duration", children: duration }), require$$0.jsx("span", { className: "tool-call-item__chevron ".concat(isExpanded ? "tool-call-item__chevron--expanded" : ""), children: "\u25BE" })] }), isExpanded && (require$$0.jsxs("div", { className: "tool-call-item__content", children: [hasInput && (require$$0.jsxs("div", { className: "tool-call-item__section", children: [require$$0.jsxs("div", { className: "tool-call-item__section-header", children: [require$$0.jsx("span", { className: "tool-call-item__section-label", children: "Input" }), require$$0.jsx(CopyButton, { text: formatJson(toolCall.input) })] }), require$$0.jsx("pre", { className: "tool-call-item__code", children: formatJson(toolCall.input) })] })), hasResult && (require$$0.jsxs("div", { className: "tool-call-item__section", children: [require$$0.jsxs("div", { className: "tool-call-item__section-header", children: [require$$0.jsx("span", { className: "tool-call-item__section-label", children: "Output" }), require$$0.jsx(CopyButton, { text: formatJson(toolCall.result) })] }), require$$0.jsx("pre", { className: "tool-call-item__code", children: formatJson(toolCall.result) })] })), hasError && (require$$0.jsxs("div", { className: "tool-call-item__section", children: [require$$0.jsxs("div", { className: "tool-call-item__section-header", children: [require$$0.jsx("span", { className: "tool-call-item__section-label tool-call-item__section-label--error", children: "Error" }), require$$0.jsx(CopyButton, { text: toolCall.error || "" })] }), require$$0.jsx("pre", { className: "tool-call-item__code tool-call-item__code--error", children: toolCall.error })] })), !hasInput && !hasResult && !hasError && (require$$0.jsx("div", { className: "tool-call-item__empty", children: "No data available" }))] }))] }));
|
|
11327
|
+
};
|
|
11328
|
+
var ToolCallDisplay = function (_a) {
|
|
11329
|
+
var toolCalls = _a.toolCalls, _b = _a.debugMode, debugMode = _b === void 0 ? false : _b;
|
|
11330
|
+
if (!toolCalls || toolCalls.length === 0) {
|
|
11331
|
+
return null;
|
|
11332
|
+
}
|
|
11333
|
+
// Debug mode: show all tools with full expandable details
|
|
11334
|
+
if (debugMode) {
|
|
11335
|
+
return (require$$0.jsxs("div", { className: "tool-call-display", children: [require$$0.jsxs("div", { className: "tool-call-display__header", children: [require$$0.jsx("span", { className: "tool-call-display__icon", children: "\u2699" }), require$$0.jsxs("span", { className: "tool-call-display__title", children: ["Tool Calls (", toolCalls.length, ")"] })] }), require$$0.jsx("div", { className: "tool-call-display__list", children: toolCalls.map(function (toolCall, index) { return (require$$0.jsx(ToolCallItem, { toolCall: toolCall }, "".concat(toolCall.name, "-").concat(toolCall.startTime, "-").concat(index))); }) })] }));
|
|
11336
|
+
}
|
|
11337
|
+
// Normal mode: filter out hidden tools and show user-friendly progress
|
|
11338
|
+
var visibleToolCalls = toolCalls.filter(function (tc) { return !tc.hidden; });
|
|
11339
|
+
if (visibleToolCalls.length === 0) {
|
|
11340
|
+
return null;
|
|
11341
|
+
}
|
|
11342
|
+
return (require$$0.jsx("div", { className: "tool-progress-display", children: visibleToolCalls.map(function (toolCall, index) { return (require$$0.jsx(ToolProgressItem, { toolCall: toolCall }, "".concat(toolCall.name, "-").concat(toolCall.startTime, "-").concat(index))); }) }));
|
|
11343
|
+
};
|
|
11344
|
+
|
|
11345
|
+
/**
|
|
11346
|
+
* Parses basic markdown to HTML.
|
|
11347
|
+
* Supports: bold, italic, links, headers, and unordered lists.
|
|
11348
|
+
*/
|
|
11349
|
+
function parseMarkdown(text) {
|
|
11350
|
+
if (!text)
|
|
11351
|
+
return "";
|
|
11352
|
+
var html = text
|
|
11353
|
+
// Escape HTML entities first
|
|
11354
|
+
.replace(/&/g, "&")
|
|
11355
|
+
.replace(/</g, "<")
|
|
11356
|
+
.replace(/>/g, ">")
|
|
11357
|
+
// Headers (must be at start of line)
|
|
11358
|
+
.replace(/^### (.+)$/gm, "<h4>$1</h4>")
|
|
11359
|
+
.replace(/^## (.+)$/gm, "<h3>$1</h3>")
|
|
11360
|
+
.replace(/^# (.+)$/gm, "<h2>$1</h2>")
|
|
11361
|
+
// Bold (** or __)
|
|
11362
|
+
.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>")
|
|
11363
|
+
.replace(/__(.+?)__/g, "<strong>$1</strong>")
|
|
11364
|
+
// Italic (* or _) - must come after bold
|
|
11365
|
+
.replace(/\*([^*]+?)\*/g, "<em>$1</em>")
|
|
11366
|
+
.replace(/_([^_]+?)_/g, "<em>$1</em>")
|
|
11367
|
+
// Links [text](url)
|
|
11368
|
+
.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>')
|
|
11369
|
+
// Unordered lists (- or *)
|
|
11370
|
+
.replace(/^[-*] (.+)$/gm, "<li>$1</li>")
|
|
11371
|
+
// Line breaks
|
|
11372
|
+
.replace(/\n/g, "<br>");
|
|
11373
|
+
// Wrap consecutive <li> elements in <ul>
|
|
11374
|
+
html = html.replace(/(<li>.*?<\/li>)(<br>)?/g, "$1");
|
|
11375
|
+
html = html.replace(/(<li>.*?<\/li>)+/g, "<ul>$&</ul>");
|
|
11376
|
+
// Clean up <br> inside <ul>
|
|
11377
|
+
html = html.replace(/<ul>(.*?)<\/ul>/g, function (match) {
|
|
11378
|
+
return match.replace(/<br>/g, "");
|
|
11379
|
+
});
|
|
11380
|
+
return html;
|
|
11381
|
+
}
|
|
9530
11382
|
var ChatTextMessage = function (props) {
|
|
11383
|
+
var _a;
|
|
9531
11384
|
var message = props.message;
|
|
9532
11385
|
var date = new Date(message.timestamp);
|
|
9533
11386
|
var time = date.getHours() + ":" + date.getMinutes();
|
|
9534
|
-
var agentMessage = isAgent(props.message.user.nick);
|
|
9535
|
-
|
|
11387
|
+
var agentMessage = isAgent((_a = props.message.user) === null || _a === void 0 ? void 0 : _a.nick);
|
|
11388
|
+
var isStreaming = message.isStreaming;
|
|
11389
|
+
// Parse markdown for agent messages
|
|
11390
|
+
var parsedHtml = require$$1.useMemo(function () {
|
|
11391
|
+
if (agentMessage && message.msg.text) {
|
|
11392
|
+
return parseMarkdown(message.msg.text);
|
|
11393
|
+
}
|
|
11394
|
+
return null;
|
|
11395
|
+
}, [agentMessage, message.msg.text]);
|
|
11396
|
+
// No tail for streaming messages (or messages that were streamed)
|
|
11397
|
+
var wasStreamed = message.wasStreamed;
|
|
11398
|
+
var showTail = agentMessage && !props.sibling && !isStreaming && !wasStreamed;
|
|
11399
|
+
var hasToolCalls = props.toolCalls && props.toolCalls.length > 0;
|
|
11400
|
+
return (require$$0.jsxs("div", { className: "chat-msg".concat(isStreaming ? " chat-msg--streaming" : ""), children: [hasToolCalls && (require$$0.jsx(ToolCallDisplay, { toolCalls: props.toolCalls, debugMode: props.debugMode })), require$$0.jsx("div", { className: "chat-text-container", children: require$$0.jsxs(ChatMessageBubble, { owner: agentMessage ? "others" : "mine", hasTail: showTail, children: [require$$0.jsx("span", { className: "message-sr-only", children: "at " + time + (agentMessage ? (isStreaming ? " the bot is typing" : " the bot said") : " the user said") }), parsedHtml ? (require$$0.jsx("span", { className: "chat-msg__content", dangerouslySetInnerHTML: { __html: parsedHtml } })) : (require$$0.jsx("span", { className: "chat-msg__content", children: message.msg.text })), isStreaming && require$$0.jsx("span", { className: "streaming-cursor", "aria-hidden": "true" })] }) })] }));
|
|
9536
11401
|
};
|
|
9537
11402
|
|
|
9538
11403
|
var ChatScheduleWidget = function (props) {
|
|
@@ -9546,6 +11411,24 @@ var ChatScheduleWidget = function (props) {
|
|
|
9546
11411
|
return (require$$0.jsx("div", { className: "chat-schedule-button-container", children: require$$0.jsxs("button", { className: "chat-schedule-button", onClick: handleClick, children: [require$$0.jsx("i", { className: "fa fa-lg fa-calendar" }), require$$0.jsx("span", { children: display.label || "Schedule Now!" })] }) }));
|
|
9547
11412
|
};
|
|
9548
11413
|
|
|
11414
|
+
/**
|
|
11415
|
+
* Displays source links below a chat message.
|
|
11416
|
+
* Designed to be compact and unobtrusive, unlike suggestion chips.
|
|
11417
|
+
*/
|
|
11418
|
+
var SourceLinks = function (props) {
|
|
11419
|
+
var sources = props.sources, _a = props.prefix, prefix = _a === void 0 ? "Sources:" : _a, onSourceClick = props.onSourceClick;
|
|
11420
|
+
if (!sources || sources.length === 0) {
|
|
11421
|
+
return null;
|
|
11422
|
+
}
|
|
11423
|
+
var handleClick = function (source, e) {
|
|
11424
|
+
if (onSourceClick) {
|
|
11425
|
+
e.preventDefault();
|
|
11426
|
+
onSourceClick(source);
|
|
11427
|
+
}
|
|
11428
|
+
};
|
|
11429
|
+
return (require$$0.jsxs("div", { className: "source-links", children: [prefix && require$$0.jsx("span", { className: "source-links__prefix", children: prefix }), require$$0.jsx("span", { className: "source-links__list", children: sources.map(function (source, index) { return (require$$0.jsxs("span", { className: "source-links__item", children: [require$$0.jsx("a", { href: source.url, target: source.newTab !== false ? "_blank" : "_self", rel: source.newTab !== false ? "noopener noreferrer" : undefined, className: "source-links__link", onClick: function (e) { return handleClick(source, e); }, title: source.url, children: source.title }), index < sources.length - 1 && (require$$0.jsx("span", { className: "source-links__separator", children: "\u00B7" }))] }, index)); }) })] }));
|
|
11430
|
+
};
|
|
11431
|
+
|
|
9549
11432
|
function getClassName(msg) {
|
|
9550
11433
|
return isAgent(msg.user.nick) ? "agent" : "visitor";
|
|
9551
11434
|
}
|
|
@@ -9557,7 +11440,7 @@ var avaKeys = ["text", "html", "card", "list"];
|
|
|
9557
11440
|
* @returns
|
|
9558
11441
|
*/
|
|
9559
11442
|
var ChatMessage = function (props) {
|
|
9560
|
-
var _a;
|
|
11443
|
+
var _a, _b, _c;
|
|
9561
11444
|
var middleware = props.messageMiddleware || StandardMiddlewares;
|
|
9562
11445
|
var chatConfig = require$$1.useContext(ChatConfigContext);
|
|
9563
11446
|
// console.log(`########### chatConfig: ${JSON.stringify(chatConfig, null, 2)}`);
|
|
@@ -9567,7 +11450,7 @@ var ChatMessage = function (props) {
|
|
|
9567
11450
|
var agentInfo = (_a = props.agents) === null || _a === void 0 ? void 0 : _a[props.message.user.nick];
|
|
9568
11451
|
var hideUserInfo = (agentInfo === null || agentInfo === void 0 ? void 0 : agentInfo.hideUserInfo) || false;
|
|
9569
11452
|
function renderByType() {
|
|
9570
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
11453
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
|
|
9571
11454
|
var msg = props.message.msg;
|
|
9572
11455
|
switch (props.message.type) {
|
|
9573
11456
|
// TODO: props actually requires it to be "chat.msg". Fix prop typing?
|
|
@@ -9575,27 +11458,42 @@ var ChatMessage = function (props) {
|
|
|
9575
11458
|
// Here is the deal. If we have text (output speech), then text - card - list - options
|
|
9576
11459
|
// OR card OR list only. Avatar with text bubble.
|
|
9577
11460
|
var avaKey = avaKeys.find(function (key) { return !!msg[key]; });
|
|
9578
|
-
|
|
9579
|
-
|
|
9580
|
-
|
|
11461
|
+
var isStreaming_1 = props.message.isStreaming;
|
|
11462
|
+
var wasStreamed = props.message.wasStreamed;
|
|
11463
|
+
// Tool calls - show progress while tools execute (before text arrives)
|
|
11464
|
+
var toolCalls = props.message.toolCalls;
|
|
11465
|
+
var hasToolCalls = toolCalls && toolCalls.length > 0;
|
|
11466
|
+
var debugMode = (_d = (_c = (_b = (_a = chatConfig === null || chatConfig === void 0 ? void 0 : chatConfig.env) === null || _a === void 0 ? void 0 : _a.connection) === null || _b === void 0 ? void 0 : _b.mcp) === null || _c === void 0 ? void 0 : _c.debugMode) !== null && _d !== void 0 ? _d : false;
|
|
11467
|
+
// Show tool progress with avatar when no text yet, or above text when text exists
|
|
11468
|
+
var showToolProgressWithAvatar = hasToolCalls && !msg.text;
|
|
11469
|
+
return (require$$0.jsxs(require$$0.Fragment, { children: [showToolProgressWithAvatar && (require$$0.jsx(ChatMessagePart, { showAvatar: true, user: user, avatarPosition: (_g = (_f = (_e = chatConfig === null || chatConfig === void 0 ? void 0 : chatConfig.env) === null || _e === void 0 ? void 0 : _e.theme) === null || _f === void 0 ? void 0 : _f.messages) === null || _g === void 0 ? void 0 : _g.avatarPosition, hideUserInfo: hideUserInfo, isStreaming: true, children: require$$0.jsx("div", { className: "chat-msg", children: require$$0.jsx(ToolCallDisplay, { toolCalls: toolCalls, debugMode: debugMode }) }) })), msg.text && (require$$0.jsx(ChatMessagePart, { showAvatar: avaKey === "text", user: user, avatarPosition: (_k = (_j = (_h = chatConfig === null || chatConfig === void 0 ? void 0 : chatConfig.env) === null || _h === void 0 ? void 0 : _h.theme) === null || _j === void 0 ? void 0 : _j.messages) === null || _k === void 0 ? void 0 : _k.avatarPosition, hideUserInfo: hideUserInfo, isStreaming: isStreaming_1 || wasStreamed, children: require$$0.jsx(ChatTextMessage, { message: props.message, sibling: props.sibling, user: user, toolCalls: hasToolCalls ? toolCalls : undefined, debugMode: debugMode }) })), msg.html &&
|
|
11470
|
+
require$$0.jsx(ChatMessagePart, { showAvatar: avaKey === "html", user: user, avatarPosition: (_o = (_m = (_l = chatConfig === null || chatConfig === void 0 ? void 0 : chatConfig.env) === null || _l === void 0 ? void 0 : _l.theme) === null || _m === void 0 ? void 0 : _m.messages) === null || _o === void 0 ? void 0 : _o.avatarPosition, hideUserInfo: hideUserInfo, children: require$$0.jsx(ChatMarkdownMessage, { message: props.message, sibling: props.sibling, onOpenUrl: (_p = props.middlewareContext) === null || _p === void 0 ? void 0 : _p.openUrl }) }), msg.displays && middleware && msg.displays.map(function (display, index) {
|
|
9581
11471
|
if (display.type === "ScheduleButton") {
|
|
9582
11472
|
return (require$$0.jsx(ChatScheduleWidget, { minimizeOnClick: props.minimizeOnClick, display: display }));
|
|
9583
11473
|
}
|
|
9584
11474
|
var Middleware = middleware;
|
|
9585
11475
|
return (require$$0.jsx(Middleware, { msg: display, ctx: props.middlewareContext }, index));
|
|
9586
11476
|
}), msg.permissionRequest && ctx &&
|
|
9587
|
-
require$$0.jsx(ChatMessagePart, { showAvatar: avaKey === "permissionRequest", user: user, avatarPosition: (
|
|
11477
|
+
require$$0.jsx(ChatMessagePart, { showAvatar: avaKey === "permissionRequest", user: user, avatarPosition: (_s = (_r = (_q = chatConfig === null || chatConfig === void 0 ? void 0 : chatConfig.env) === null || _q === void 0 ? void 0 : _q.theme) === null || _r === void 0 ? void 0 : _r.messages) === null || _s === void 0 ? void 0 : _s.avatarPosition, hideUserInfo: hideUserInfo, children: require$$0.jsx(ChatPermissionMessage, { message: props.message, sibling: props.sibling, ctx: ctx }) })] }));
|
|
9588
11478
|
}
|
|
9589
11479
|
return (require$$0.jsx(require$$0.Fragment, {}));
|
|
9590
11480
|
}
|
|
9591
|
-
function
|
|
11481
|
+
function renderTimestampAndSources() {
|
|
11482
|
+
var _a;
|
|
9592
11483
|
var timestamp = props.message.timestamp;
|
|
9593
11484
|
var ts = new Date(timestamp);
|
|
9594
11485
|
var timeAgo = getTimeAgo(ts);
|
|
9595
|
-
|
|
9596
|
-
|
|
11486
|
+
var sources = (_a = props.message.msg) === null || _a === void 0 ? void 0 : _a.sources;
|
|
11487
|
+
var hasSources = sources && sources.length > 0;
|
|
11488
|
+
var isAgentMessage = isAgent(props.message.user.nick);
|
|
11489
|
+
return (require$$0.jsxs("div", { className: "chat-msg-footer ".concat(isAgentMessage ? "agent" : "visitor"), children: [require$$0.jsx("span", { className: "chat-msg-timestamp", children: timeAgo }), hasSources && (require$$0.jsx(SourceLinks, { sources: sources.map(function (s) { return ({ title: s.title, url: s.url, newTab: s.newTab }); }) }))] }));
|
|
11490
|
+
}
|
|
11491
|
+
// Don't show timestamp/sources while streaming or if message has no content yet
|
|
11492
|
+
var isStreaming = props.message.isStreaming;
|
|
11493
|
+
var hasContent = !!(((_b = props.message.msg) === null || _b === void 0 ? void 0 : _b.text) || ((_c = props.message.msg) === null || _c === void 0 ? void 0 : _c.html));
|
|
11494
|
+
var showFooter = !isStreaming && hasContent;
|
|
9597
11495
|
// empty
|
|
9598
|
-
return (require$$0.jsx("div", { className: "chat-msg-container-wrapper ".concat(isAgent(props.message.user.nick) ? "agent" : "visitor", " ").concat(props.sibling ? "sibling" : ""), children: require$$0.jsxs("div", { className: "chat-msg-container ".concat(getClassName(props.message)), children: [renderByType(),
|
|
11496
|
+
return (require$$0.jsx("div", { className: "chat-msg-container-wrapper ".concat(isAgent(props.message.user.nick) ? "agent" : "visitor", " ").concat(props.sibling ? "sibling" : ""), children: require$$0.jsxs("div", { className: "chat-msg-container ".concat(getClassName(props.message)), children: [renderByType(), showFooter && renderTimestampAndSources()] }) }));
|
|
9599
11497
|
};
|
|
9600
11498
|
|
|
9601
11499
|
/**
|
|
@@ -33547,7 +35445,7 @@ var ChatWidgetConnected = function (props) {
|
|
|
33547
35445
|
* Exported for use by StaticChatWidgetContainer to avoid infinite loops.
|
|
33548
35446
|
*/
|
|
33549
35447
|
var ChatWidgetUI = function (props) {
|
|
33550
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9;
|
|
35448
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14;
|
|
33551
35449
|
var innerDispatch = useChatDispatch();
|
|
33552
35450
|
var dispatch = useChatServerDispatch();
|
|
33553
35451
|
// From Redux
|
|
@@ -33577,12 +35475,12 @@ var ChatWidgetUI = function (props) {
|
|
|
33577
35475
|
chatState.visuals = {};
|
|
33578
35476
|
}
|
|
33579
35477
|
// Our state - pull from storage
|
|
33580
|
-
var
|
|
35478
|
+
var _15 = require$$1.useState((!canMinimize && !canCancel) ||
|
|
33581
35479
|
// !!get("visible") ||
|
|
33582
35480
|
chatState.visuals.visible ||
|
|
33583
35481
|
(((_m = props.config) === null || _m === void 0 ? void 0 : _m.autoOpenOnWidth) &&
|
|
33584
|
-
window.matchMedia("(min-width: ".concat((_o = props.config) === null || _o === void 0 ? void 0 : _o.autoOpenOnWidth, ")")).matches)), visible =
|
|
33585
|
-
var
|
|
35482
|
+
window.matchMedia("(min-width: ".concat((_o = props.config) === null || _o === void 0 ? void 0 : _o.autoOpenOnWidth, ")")).matches)), visible = _15[0], setVisibleState = _15[1];
|
|
35483
|
+
var _16 = require$$1.useState(false); _16[0]; var setTypingState = _16[1]; // false initially - state kept for potential external observers
|
|
33586
35484
|
// Ref to track typing state for use in timeout callbacks
|
|
33587
35485
|
var typingRef = require$$1.useRef(false);
|
|
33588
35486
|
// Timeout ref for debouncing "stop typing" events
|
|
@@ -33628,7 +35526,7 @@ var ChatWidgetUI = function (props) {
|
|
|
33628
35526
|
};
|
|
33629
35527
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
33630
35528
|
}, []);
|
|
33631
|
-
var
|
|
35529
|
+
var _17 = require$$1.useState(!document.hidden), isTabVisible = _17[0], setIsTabVisible = _17[1];
|
|
33632
35530
|
require$$1.useEffect(function () {
|
|
33633
35531
|
var handleVisibilityChange = function () {
|
|
33634
35532
|
setIsTabVisible(!document.hidden);
|
|
@@ -33840,8 +35738,8 @@ var ChatWidgetUI = function (props) {
|
|
|
33840
35738
|
}));
|
|
33841
35739
|
}
|
|
33842
35740
|
// Action Bar state and handlers
|
|
33843
|
-
var
|
|
33844
|
-
var
|
|
35741
|
+
var _18 = require$$1.useState(false), formModalOpen = _18[0], setFormModalOpen = _18[1];
|
|
35742
|
+
var _19 = require$$1.useState(null), activeFormConfig = _19[0], setActiveFormConfig = _19[1];
|
|
33845
35743
|
var handleFormButtonClick = require$$1.useCallback(function (formButton) {
|
|
33846
35744
|
// If form-widget is configured, use its openForm API
|
|
33847
35745
|
// The form-widget script should already be pre-loaded
|
|
@@ -33955,7 +35853,9 @@ var ChatWidgetUI = function (props) {
|
|
|
33955
35853
|
avatarPath: config.avatarUrl,
|
|
33956
35854
|
display_name: "Agent",
|
|
33957
35855
|
};
|
|
33958
|
-
return (require$$0.jsxs(require$$0.Fragment, { children: [require$$0.jsxs("div", { className: "widget-container ".concat(modeClass, " ").concat(getVisibilityClass()).concat(actionBarEnabled ? " widget-container--with-action-bar" : ""), children: [require$$0.jsx(WidgetStylesheet, { theme: config === null || config === void 0 ? void 0 : config.theme }), require$$0.jsx(ChatHeader, { accountStatus: chatState.accountStatus, refreshOnClick: handleRestartClick, minimizeOnClick: handleMinimizeClick, cancelOnClick: handleCancelClick, agent: widgetAgent, canRefresh: canRefresh, canMinimize: canMinimize, canCancel: canCancel, config: config === null || config === void 0 ? void 0 : config.header, menuConfig: config.menu, onSubmit: handleOnSubmit }), require$$0.jsx(MessageList, { visible: visible, queuePosition: chatState.queuePosition, isChatting: chatState.isChatting, isOffline: isOffline, messages: messages, agents: chatState.agents, agent: config === null || config === void 0 ? void 0 : config.agent, lastRatingRequestTimestamp: chatState.lastRatingRequestTimestamp, hasRating: chatState.hasRating, visitorId: chatState.visitorId, hasWsButton: !!chatState.wsButton, messageMiddleware: props.messageMiddleware, textTypingStatusEnabled: (
|
|
35856
|
+
return (require$$0.jsxs(require$$0.Fragment, { children: [require$$0.jsxs("div", { className: "widget-container ".concat(modeClass, " ").concat(getVisibilityClass()).concat(actionBarEnabled ? " widget-container--with-action-bar" : ""), children: [require$$0.jsx(WidgetStylesheet, { theme: config === null || config === void 0 ? void 0 : config.theme }), require$$0.jsx(ChatHeader, { accountStatus: chatState.accountStatus, refreshOnClick: handleRestartClick, minimizeOnClick: handleMinimizeClick, cancelOnClick: handleCancelClick, agent: widgetAgent, canRefresh: canRefresh, canMinimize: canMinimize, canCancel: canCancel, config: config === null || config === void 0 ? void 0 : config.header, menuConfig: config.menu, debugMode: (_y = (_x = (_w = props.config) === null || _w === void 0 ? void 0 : _w.connection) === null || _x === void 0 ? void 0 : _x.mcp) === null || _y === void 0 ? void 0 : _y.debugMode, onSubmit: handleOnSubmit }), require$$0.jsx(MessageList, { visible: visible, queuePosition: chatState.queuePosition, isChatting: chatState.isChatting, isOffline: isOffline, messages: messages, agents: chatState.agents, agent: config === null || config === void 0 ? void 0 : config.agent, lastRatingRequestTimestamp: chatState.lastRatingRequestTimestamp, hasRating: chatState.hasRating, visitorId: chatState.visitorId, hasWsButton: !!chatState.wsButton, messageMiddleware: props.messageMiddleware, textTypingStatusEnabled: (_0 = (_z = props.config) === null || _z === void 0 ? void 0 : _z.typingStatus) === null || _0 === void 0 ? void 0 : _0.textTypingStatusEnabled, disableAutoScroll: props.disableAutoScroll, onSend: handleSendMessage, onWrite: handleWriteMessage, onOpenUrl: handleOpenUrl, minimizeOnClick: handleMinimizeClick }), require$$0.jsx("div", { className: "xa-spinner-container ".concat(visible && connectionStatus === "pending" ? "visible" : ""), children: require$$0.jsx("div", { className: "xa-spinner" }) }), connectionStatus === "offline" && require$$0.jsx(ServerOffline, {}), chatState.wsButton && visible && (require$$0.jsx(WsButton, { button: chatState.wsButton, onPress: handleWsButtonPress })), require$$0.jsx(ChatFooter, { isAdmin: config === null || config === void 0 ? void 0 : config.isAdmin, isChatting: chatState.isChatting, placeholder: (_1 = config === null || config === void 0 ? void 0 : config.input) === null || _1 === void 0 ? void 0 : _1.placeholder, sendButtonIcon: (_3 = (_2 = config === null || config === void 0 ? void 0 : config.footer) === null || _2 === void 0 ? void 0 : _2.sendButton) === null || _3 === void 0 ? void 0 : _3.icon, sendButtonIconHover: (_5 = (_4 = config === null || config === void 0 ? void 0 : config.footer) === null || _4 === void 0 ? void 0 : _4.sendButton) === null || _5 === void 0 ? void 0 : _5.iconHover, sendButtonIconDisabled: (_7 = (_6 = config === null || config === void 0 ? void 0 : config.footer) === null || _6 === void 0 ? void 0 : _6.sendButton) === null || _7 === void 0 ? void 0 : _7.iconDisabled, visible: visible, hasWsButton: !!chatState.wsButton, menuConfig: (_8 = props.config) === null || _8 === void 0 ? void 0 : _8.menu, footerConfig: (_9 = props.config) === null || _9 === void 0 ? void 0 : _9.footer, inputConfig: (_10 = props.config) === null || _10 === void 0 ? void 0 : _10.input, disabled: previewMode, onChange: handleOnChange, onSubmit: handleOnSubmit, onFileUpload: handleFileUpload }), require$$0.jsx("div", { className: "restartModal", ref: modalRef, onClick: handleRestartModalCloseClick, children: require$$0.jsx(ModalContent, { onClose: handleRestartModalCloseClick, onReset: handleReset }) })] }), actionBarEnabled && config.actionBar ? (require$$0.jsxs(require$$0.Fragment, { children: [require$$0.jsx(ActionBar, { config: __assign(__assign({}, config.actionBar), {
|
|
35857
|
+
// Use actionBar.cta if set, otherwise fall back to widget-level cta
|
|
35858
|
+
cta: (_11 = config.actionBar.cta) !== null && _11 !== void 0 ? _11 : config.cta }), visible: visible, chatDisabled: config.disabled, hasUserInteracted: (_12 = chatState.visuals) === null || _12 === void 0 ? void 0 : _12.hasInteracted, onChatClick: chatButtonOnClick, onChatMinimize: handleMinimizeClick, onFormClick: handleFormButtonClick, onCtaDismiss: handleCtaDismiss }), formModalOpen && activeFormConfig && (require$$0.jsx(FormModal, { config: activeFormConfig, widgetEnv: config, onClose: handleFormModalClose }))] })) : (require$$0.jsx(ChatButton, { addClass: visible ? "visible" : "", onClick: chatButtonOnClick, config: config === null || config === void 0 ? void 0 : config.cta, imageUrl: (_13 = config === null || config === void 0 ? void 0 : config.chatButton) === null || _13 === void 0 ? void 0 : _13.imageUrl, visible: visible, hasInteracted: (_14 = chatState.visuals) === null || _14 === void 0 ? void 0 : _14.hasInteracted, onCtaDismiss: handleCtaDismiss })), require$$0.jsx(ErrorOverlay, { enableErrorOverlay: config === null || config === void 0 ? void 0 : config.enableErrorOverlay })] }));
|
|
33959
35859
|
};
|
|
33960
35860
|
/**
|
|
33961
35861
|
* Top-level wrapper that dispatches between preview mode and connected mode.
|
|
@@ -34137,6 +36037,7 @@ function memberLeave(state, detail) {
|
|
|
34137
36037
|
}
|
|
34138
36038
|
|
|
34139
36039
|
function resetReducer(state) {
|
|
36040
|
+
var _a;
|
|
34140
36041
|
if (state === void 0) { state = DEFAULT_STATE; }
|
|
34141
36042
|
var defaultState = createDefaultState({
|
|
34142
36043
|
accessToken: state.accessToken,
|
|
@@ -34146,6 +36047,8 @@ function resetReducer(state) {
|
|
|
34146
36047
|
});
|
|
34147
36048
|
// If we have an active connection, preserve status to avoid showing offline during reconnect
|
|
34148
36049
|
var wasOnline = state.connection.connectionStatus === 'online';
|
|
36050
|
+
// Preserve opened state so widget doesn't close on refresh
|
|
36051
|
+
var wasOpened = (_a = state.visuals) === null || _a === void 0 ? void 0 : _a.opened;
|
|
34149
36052
|
return __assign(__assign({}, defaultState), { connection: __assign(__assign({}, defaultState.connection), { greetingRequested: false,
|
|
34150
36053
|
// Generate new nonce to trigger server recreation and start fresh session
|
|
34151
36054
|
nonce: uuid_1(),
|
|
@@ -34153,8 +36056,11 @@ function resetReducer(state) {
|
|
|
34153
36056
|
connectionStatus: wasOnline ? 'online' : defaultState.connection.connectionStatus }),
|
|
34154
36057
|
// Preserve accountStatus if we were online to avoid showing offline message during reconnect
|
|
34155
36058
|
accountStatus: wasOnline ? 'online' : defaultState.accountStatus, visitor: state.visitor, visitorId: state.visitorId,
|
|
34156
|
-
//
|
|
34157
|
-
visuals: {
|
|
36059
|
+
// Reset visuals but preserve opened state so widget stays open during refresh
|
|
36060
|
+
visuals: {
|
|
36061
|
+
opened: wasOpened,
|
|
36062
|
+
hasInteracted: false
|
|
36063
|
+
} });
|
|
34158
36064
|
}
|
|
34159
36065
|
|
|
34160
36066
|
// Type guard for ChatSystemMessageDetail
|
|
@@ -34170,7 +36076,7 @@ function appendMessageToState(state, msg) {
|
|
|
34170
36076
|
}
|
|
34171
36077
|
function update(state, action) {
|
|
34172
36078
|
var _a, _b, _c, _d;
|
|
34173
|
-
var _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v;
|
|
36079
|
+
var _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w;
|
|
34174
36080
|
if (state === void 0) { state = DEFAULT_STATE; }
|
|
34175
36081
|
log("action", action);
|
|
34176
36082
|
if (action.type === "reset") {
|
|
@@ -34278,6 +36184,121 @@ function update(state, action) {
|
|
|
34278
36184
|
case "chat.typing":
|
|
34279
36185
|
var agent = state.agents[action.detail.user.nick];
|
|
34280
36186
|
return __assign(__assign({}, state), { lastTimestamp: (_v = action.detail) === null || _v === void 0 ? void 0 : _v.timestamp, agents: __assign(__assign({}, state.agents), (_d = {}, _d[action.detail.user.nick] = __assign(__assign({}, agent), { typing: action.detail.typing }), _d)) });
|
|
36187
|
+
case "chat.stream.start":
|
|
36188
|
+
// Add placeholder message with streaming state
|
|
36189
|
+
var streamingMsg = {
|
|
36190
|
+
type: "chat.msg",
|
|
36191
|
+
user: action.detail.user,
|
|
36192
|
+
timestamp: action.detail.timestamp,
|
|
36193
|
+
msg: {
|
|
36194
|
+
text: "",
|
|
36195
|
+
},
|
|
36196
|
+
isStreaming: true,
|
|
36197
|
+
};
|
|
36198
|
+
return __assign(__assign({}, newState), { chats: __spreadArray$1(__spreadArray$1([], newState.chats, true), [streamingMsg], false), streamingMessageId: action.detail.messageId });
|
|
36199
|
+
case "chat.stream.chunk": {
|
|
36200
|
+
// Find the streaming message by ID and append text
|
|
36201
|
+
var chunkDetail = action.detail;
|
|
36202
|
+
if (state.streamingMessageId === chunkDetail.messageId) {
|
|
36203
|
+
// Find the streaming message by ID (not by index - other messages could be inserted)
|
|
36204
|
+
var streamingIndex_1 = state.chats.findIndex(function (chat) { return isChatMsgDetail(chat) && chat.isStreaming; });
|
|
36205
|
+
if (streamingIndex_1 >= 0) {
|
|
36206
|
+
var chunkText_1 = chunkDetail.text;
|
|
36207
|
+
return __assign(__assign({}, state), { lastTimestamp: chunkDetail === null || chunkDetail === void 0 ? void 0 : chunkDetail.timestamp, chats: state.chats.map(function (chat, index) {
|
|
36208
|
+
var _a;
|
|
36209
|
+
if (index === streamingIndex_1 && isChatMsgDetail(chat)) {
|
|
36210
|
+
return __assign(__assign({}, chat), { msg: __assign(__assign({}, chat.msg), { text: (((_a = chat.msg) === null || _a === void 0 ? void 0 : _a.text) || "") + chunkText_1 }) });
|
|
36211
|
+
}
|
|
36212
|
+
return chat;
|
|
36213
|
+
}) });
|
|
36214
|
+
}
|
|
36215
|
+
}
|
|
36216
|
+
return state;
|
|
36217
|
+
}
|
|
36218
|
+
case "chat.stream.tool_start": {
|
|
36219
|
+
// Add tool call to the streaming message (in-progress state)
|
|
36220
|
+
log("Tool execution started: ".concat(action.detail.toolName));
|
|
36221
|
+
var toolStartDetail_1 = action.detail;
|
|
36222
|
+
if (state.streamingMessageId === toolStartDetail_1.messageId) {
|
|
36223
|
+
// Find the streaming message by ID (not by index - other messages could be inserted)
|
|
36224
|
+
var streamingIndex_2 = state.chats.findIndex(function (chat) { return isChatMsgDetail(chat) && chat.isStreaming; });
|
|
36225
|
+
if (streamingIndex_2 >= 0) {
|
|
36226
|
+
return __assign(__assign({}, state), { lastTimestamp: toolStartDetail_1.timestamp, chats: state.chats.map(function (chat, index) {
|
|
36227
|
+
if (index === streamingIndex_2 && isChatMsgDetail(chat)) {
|
|
36228
|
+
var existingToolCalls = chat.toolCalls || [];
|
|
36229
|
+
return __assign(__assign({}, chat), { toolCalls: __spreadArray$1(__spreadArray$1([], existingToolCalls, true), [
|
|
36230
|
+
{
|
|
36231
|
+
name: toolStartDetail_1.toolName,
|
|
36232
|
+
toolCallId: toolStartDetail_1.toolCallId,
|
|
36233
|
+
displayName: toolStartDetail_1.displayName,
|
|
36234
|
+
label: toolStartDetail_1.label,
|
|
36235
|
+
hidden: toolStartDetail_1.hidden,
|
|
36236
|
+
input: toolStartDetail_1.toolInput,
|
|
36237
|
+
startTime: toolStartDetail_1.timestamp,
|
|
36238
|
+
isExecuting: true,
|
|
36239
|
+
},
|
|
36240
|
+
], false) });
|
|
36241
|
+
}
|
|
36242
|
+
return chat;
|
|
36243
|
+
}) });
|
|
36244
|
+
}
|
|
36245
|
+
}
|
|
36246
|
+
return state;
|
|
36247
|
+
}
|
|
36248
|
+
case "chat.stream.tool_end": {
|
|
36249
|
+
// Update tool call with result in the streaming message
|
|
36250
|
+
log("Tool execution ended: ".concat(action.detail.toolName));
|
|
36251
|
+
var toolEndDetail_1 = action.detail;
|
|
36252
|
+
if (state.streamingMessageId === toolEndDetail_1.messageId) {
|
|
36253
|
+
// Find the streaming message by ID (not by index - other messages could be inserted)
|
|
36254
|
+
var streamingIndex_3 = state.chats.findIndex(function (chat) { return isChatMsgDetail(chat) && chat.isStreaming; });
|
|
36255
|
+
if (streamingIndex_3 >= 0) {
|
|
36256
|
+
return __assign(__assign({}, state), { lastTimestamp: toolEndDetail_1.timestamp, chats: state.chats.map(function (chat, index) {
|
|
36257
|
+
var _a;
|
|
36258
|
+
if (index === streamingIndex_3 && isChatMsgDetail(chat)) {
|
|
36259
|
+
var toolCalls = (_a = chat.toolCalls) === null || _a === void 0 ? void 0 : _a.map(function (tc) {
|
|
36260
|
+
var _a;
|
|
36261
|
+
// Match by toolCallId if available, otherwise fall back to name + isExecuting
|
|
36262
|
+
var isMatch = toolEndDetail_1.toolCallId
|
|
36263
|
+
? tc.toolCallId === toolEndDetail_1.toolCallId
|
|
36264
|
+
: tc.name === toolEndDetail_1.toolName && tc.isExecuting;
|
|
36265
|
+
if (isMatch) {
|
|
36266
|
+
return __assign(__assign({}, tc), { displayName: toolEndDetail_1.displayName || tc.displayName, label: toolEndDetail_1.label, hidden: (_a = toolEndDetail_1.hidden) !== null && _a !== void 0 ? _a : tc.hidden, result: toolEndDetail_1.toolResult, error: toolEndDetail_1.toolError, endTime: toolEndDetail_1.timestamp, isExecuting: false });
|
|
36267
|
+
}
|
|
36268
|
+
return tc;
|
|
36269
|
+
});
|
|
36270
|
+
return __assign(__assign({}, chat), { toolCalls: toolCalls });
|
|
36271
|
+
}
|
|
36272
|
+
return chat;
|
|
36273
|
+
}) });
|
|
36274
|
+
}
|
|
36275
|
+
}
|
|
36276
|
+
return state;
|
|
36277
|
+
}
|
|
36278
|
+
case "chat.stream.end": {
|
|
36279
|
+
// Finalize the streaming message
|
|
36280
|
+
var endDetail = action.detail;
|
|
36281
|
+
if (state.streamingMessageId === endDetail.messageId) {
|
|
36282
|
+
// Find the streaming message by ID (not by index - other messages could be inserted)
|
|
36283
|
+
var streamingIndex_4 = state.chats.findIndex(function (chat) { return isChatMsgDetail(chat) && chat.isStreaming; });
|
|
36284
|
+
var endStreamingChat = streamingIndex_4 >= 0 ? state.chats[streamingIndex_4] : undefined;
|
|
36285
|
+
// Verify it's the right message using type guard
|
|
36286
|
+
if (streamingIndex_4 >= 0 && endStreamingChat && isChatMsgDetail(endStreamingChat)) {
|
|
36287
|
+
var finalMsg_1 = endDetail.msg;
|
|
36288
|
+
// Preserve tool calls from either the action or the existing message
|
|
36289
|
+
var toolCalls_1 = endDetail.toolCalls || endStreamingChat.toolCalls;
|
|
36290
|
+
// Update chips from message options (same as chat.msg handler)
|
|
36291
|
+
var chips = ((_w = finalMsg_1 === null || finalMsg_1 === void 0 ? void 0 : finalMsg_1.options) === null || _w === void 0 ? void 0 : _w.length) ? finalMsg_1.options : [];
|
|
36292
|
+
return __assign(__assign({}, state), { lastTimestamp: endDetail === null || endDetail === void 0 ? void 0 : endDetail.timestamp, streamingMessageId: undefined, chips: chips, chats: state.chats.map(function (chat, index) {
|
|
36293
|
+
if (index === streamingIndex_4 && isChatMsgDetail(chat)) {
|
|
36294
|
+
return __assign(__assign({}, chat), { msg: finalMsg_1, isStreaming: false, wasStreamed: true, toolCalls: toolCalls_1 });
|
|
36295
|
+
}
|
|
36296
|
+
return chat;
|
|
36297
|
+
}) });
|
|
36298
|
+
}
|
|
36299
|
+
}
|
|
36300
|
+
return state;
|
|
36301
|
+
}
|
|
34281
36302
|
default:
|
|
34282
36303
|
return state;
|
|
34283
36304
|
}
|