@kite-copilot/chat-panel 0.2.48 → 0.2.50
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/README.md +38 -0
- package/dist/auto.cjs +176 -150
- package/dist/auto.d.cts +1 -1
- package/dist/auto.d.ts +1 -1
- package/dist/auto.js +1 -1
- package/dist/{chunk-LOTJ3U5L.js → chunk-BLSVIF7H.js} +168 -142
- package/dist/{createKiteChat-e6BnJS6T.d.cts → createKiteChat-DeQKgFyx.d.cts} +8 -2
- package/dist/{createKiteChat-e6BnJS6T.d.ts → createKiteChat-DeQKgFyx.d.ts} +8 -2
- package/dist/embed.global.js +22 -22
- package/dist/index.cjs +176 -150
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -896,28 +896,22 @@ function useUserAuth({
|
|
|
896
896
|
}) {
|
|
897
897
|
const [authState, setAuthState] = React4.useState({ status: "idle" });
|
|
898
898
|
const lastSessionIdRef = React4.useRef(null);
|
|
899
|
+
const lastEnabledRef = React4.useRef(enabled);
|
|
899
900
|
const fetchUser = React4.useCallback(async () => {
|
|
900
901
|
if (!productBackendUrl || !enabled) {
|
|
901
|
-
console.log("[useUserAuth] Skipping auth - productBackendUrl:", productBackendUrl, "enabled:", enabled);
|
|
902
902
|
setAuthState({ status: "idle" });
|
|
903
903
|
return;
|
|
904
904
|
}
|
|
905
|
-
console.log("[useUserAuth] Starting auth request to product backend...");
|
|
906
|
-
console.log("[useUserAuth] Product backend URL:", productBackendUrl);
|
|
907
|
-
console.log("[useUserAuth] Full request URL:", `${productBackendUrl}/users/me`);
|
|
908
905
|
setAuthState({ status: "loading" });
|
|
909
906
|
try {
|
|
910
907
|
const response = await fetch(`${productBackendUrl}/users/me`, {
|
|
911
908
|
method: "GET",
|
|
912
909
|
credentials: "include",
|
|
913
|
-
// Include cookies for authentication
|
|
914
910
|
headers: {
|
|
915
911
|
"Accept": "application/json"
|
|
916
912
|
}
|
|
917
913
|
});
|
|
918
|
-
console.log("[useUserAuth] Response received - status:", response.status, "ok:", response.ok);
|
|
919
914
|
if (!response.ok) {
|
|
920
|
-
console.log("[useUserAuth] Auth request failed with status:", response.status);
|
|
921
915
|
if (response.status === 401) {
|
|
922
916
|
throw new Error("Please log in to use the chat assistant.");
|
|
923
917
|
}
|
|
@@ -927,27 +921,36 @@ function useUserAuth({
|
|
|
927
921
|
throw new Error(`Authentication failed (${response.status})`);
|
|
928
922
|
}
|
|
929
923
|
const user = await response.json();
|
|
930
|
-
console.log("[useUserAuth] Auth SUCCESS - parsed user data:");
|
|
931
|
-
console.log("[useUserAuth] id:", user.id);
|
|
932
|
-
console.log("[useUserAuth] email:", user.email);
|
|
933
|
-
console.log("[useUserAuth] name:", user.name);
|
|
934
|
-
console.log("[useUserAuth] role:", user.role);
|
|
935
|
-
console.log("[useUserAuth] isInternal:", user.isInternal);
|
|
936
|
-
console.log("[useUserAuth] agreementsSigned:", user.agreementsSigned);
|
|
937
|
-
console.log("[useUserAuth] lastLoginTime:", user.lastLoginTime);
|
|
938
924
|
setAuthState({ status: "authenticated", user });
|
|
939
925
|
} catch (error) {
|
|
940
|
-
|
|
926
|
+
if (error instanceof Error) {
|
|
927
|
+
const errorMessage = error.message.toLowerCase();
|
|
928
|
+
const isCorsError = errorMessage.includes("cors") || errorMessage.includes("network");
|
|
929
|
+
const is404Error = errorMessage.includes("404");
|
|
930
|
+
if (isCorsError || is404Error) {
|
|
931
|
+
console.warn("[useUserAuth] Auth endpoint unavailable, falling back to unauthenticated mode:", error.message);
|
|
932
|
+
setAuthState({ status: "idle" });
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
941
936
|
const message = error instanceof Error ? error.message : "Unable to verify your identity. Please try again.";
|
|
942
937
|
setAuthState({ status: "error", error: message });
|
|
943
938
|
}
|
|
944
939
|
}, [productBackendUrl, enabled]);
|
|
945
940
|
React4.useEffect(() => {
|
|
946
|
-
|
|
941
|
+
const sessionChanged = sessionId !== lastSessionIdRef.current;
|
|
942
|
+
const enabledChanged = enabled !== lastEnabledRef.current;
|
|
943
|
+
const becameEnabled = enabled && !lastEnabledRef.current;
|
|
944
|
+
if (sessionChanged) {
|
|
947
945
|
lastSessionIdRef.current = sessionId;
|
|
946
|
+
}
|
|
947
|
+
if (enabledChanged) {
|
|
948
|
+
lastEnabledRef.current = enabled;
|
|
949
|
+
}
|
|
950
|
+
if (sessionChanged || becameEnabled) {
|
|
948
951
|
fetchUser();
|
|
949
952
|
}
|
|
950
|
-
}, [sessionId, fetchUser]);
|
|
953
|
+
}, [sessionId, enabled, fetchUser]);
|
|
951
954
|
const retry = React4.useCallback(() => {
|
|
952
955
|
fetchUser();
|
|
953
956
|
}, [fetchUser]);
|
|
@@ -964,13 +967,11 @@ function useOrgConfig({ agentUrl, orgId }) {
|
|
|
964
967
|
});
|
|
965
968
|
React5.useEffect(() => {
|
|
966
969
|
if (!agentUrl || !orgId) {
|
|
967
|
-
console.log("[useOrgConfig] Skipping - missing agentUrl or orgId", { agentUrl, orgId });
|
|
968
970
|
return;
|
|
969
971
|
}
|
|
970
972
|
const fetchConfig = async () => {
|
|
971
973
|
setState({ status: "loading", config: null, error: null });
|
|
972
974
|
const url = `${agentUrl}/org/${orgId}/config`;
|
|
973
|
-
console.log("[useOrgConfig] Fetching org config from:", url);
|
|
974
975
|
try {
|
|
975
976
|
const response = await fetch(url, {
|
|
976
977
|
method: "GET",
|
|
@@ -982,7 +983,6 @@ function useOrgConfig({ agentUrl, orgId }) {
|
|
|
982
983
|
throw new Error(`Failed to fetch org config (${response.status})`);
|
|
983
984
|
}
|
|
984
985
|
const config = await response.json();
|
|
985
|
-
console.log("[useOrgConfig] Received config:", config);
|
|
986
986
|
setState({ status: "success", config, error: null });
|
|
987
987
|
} catch (error) {
|
|
988
988
|
const message = error instanceof Error ? error.message : "Failed to fetch org config";
|
|
@@ -995,6 +995,75 @@ function useOrgConfig({ agentUrl, orgId }) {
|
|
|
995
995
|
return state;
|
|
996
996
|
}
|
|
997
997
|
|
|
998
|
+
// src/hooks/useFrontendToolExecutor.ts
|
|
999
|
+
import { useCallback as useCallback3 } from "react";
|
|
1000
|
+
function useFrontendToolExecutor({
|
|
1001
|
+
productBackendUrl,
|
|
1002
|
+
agentUrl,
|
|
1003
|
+
sessionId
|
|
1004
|
+
}) {
|
|
1005
|
+
const sendToolResult = useCallback3(async (payload) => {
|
|
1006
|
+
try {
|
|
1007
|
+
await fetch(`${agentUrl}/chat/tool-result`, {
|
|
1008
|
+
method: "POST",
|
|
1009
|
+
headers: {
|
|
1010
|
+
"Content-Type": "application/json"
|
|
1011
|
+
},
|
|
1012
|
+
body: JSON.stringify(payload)
|
|
1013
|
+
});
|
|
1014
|
+
} catch (error) {
|
|
1015
|
+
console.error("[FrontendToolExecutor] Failed to send tool result:", error);
|
|
1016
|
+
}
|
|
1017
|
+
}, [agentUrl]);
|
|
1018
|
+
const executeToolRequest = useCallback3(async (toolRequest) => {
|
|
1019
|
+
const { call_id, arguments: args, endpoint, method, path_params } = toolRequest;
|
|
1020
|
+
try {
|
|
1021
|
+
let url = endpoint;
|
|
1022
|
+
for (const param of path_params) {
|
|
1023
|
+
if (args[param]) {
|
|
1024
|
+
url = url.replace(`{${param}}`, encodeURIComponent(args[param]));
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
const queryParams = new URLSearchParams();
|
|
1028
|
+
for (const [key, value] of Object.entries(args)) {
|
|
1029
|
+
if (!path_params.includes(key) && value !== void 0 && value !== null) {
|
|
1030
|
+
queryParams.append(key, String(value));
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
const queryString = queryParams.toString();
|
|
1034
|
+
const fullUrl = `${productBackendUrl}${url}${queryString ? "?" + queryString : ""}`;
|
|
1035
|
+
const response = await fetch(fullUrl, {
|
|
1036
|
+
method,
|
|
1037
|
+
credentials: "include",
|
|
1038
|
+
headers: {
|
|
1039
|
+
"Accept": "application/json",
|
|
1040
|
+
"Content-Type": "application/json"
|
|
1041
|
+
}
|
|
1042
|
+
});
|
|
1043
|
+
let result;
|
|
1044
|
+
if (response.ok) {
|
|
1045
|
+
result = await response.json();
|
|
1046
|
+
} else {
|
|
1047
|
+
const errorText = await response.text();
|
|
1048
|
+
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
1049
|
+
}
|
|
1050
|
+
await sendToolResult({
|
|
1051
|
+
session_id: sessionId,
|
|
1052
|
+
call_id,
|
|
1053
|
+
result
|
|
1054
|
+
});
|
|
1055
|
+
} catch (error) {
|
|
1056
|
+
console.error("[FrontendToolExecutor] Tool execution failed:", error);
|
|
1057
|
+
await sendToolResult({
|
|
1058
|
+
session_id: sessionId,
|
|
1059
|
+
call_id,
|
|
1060
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
1063
|
+
}, [productBackendUrl, sessionId, sendToolResult]);
|
|
1064
|
+
return { executeToolRequest };
|
|
1065
|
+
}
|
|
1066
|
+
|
|
998
1067
|
// src/components/TypingIndicator.tsx
|
|
999
1068
|
import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1000
1069
|
function TypingIndicator({ className = "" }) {
|
|
@@ -1025,7 +1094,6 @@ function TypingIndicator({ className = "" }) {
|
|
|
1025
1094
|
|
|
1026
1095
|
// src/ChatPanel.tsx
|
|
1027
1096
|
import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1028
|
-
var CHAT_PANEL_VERSION = true ? "0.2.48" : "dev";
|
|
1029
1097
|
var DEFAULT_AGENT_URL = "http://localhost:5002";
|
|
1030
1098
|
var PANEL_WIDTH = 400;
|
|
1031
1099
|
var PANEL_HEIGHT = 600;
|
|
@@ -1489,7 +1557,8 @@ function ChatPanel({
|
|
|
1489
1557
|
supabaseAnonKey,
|
|
1490
1558
|
initialCorner = "bottom-left",
|
|
1491
1559
|
onCornerChange,
|
|
1492
|
-
productBackendUrl
|
|
1560
|
+
productBackendUrl,
|
|
1561
|
+
getAuthHeaders
|
|
1493
1562
|
} = {}) {
|
|
1494
1563
|
const [messages, setMessages] = React6.useState(initialMessages);
|
|
1495
1564
|
const [input, setInput] = React6.useState("");
|
|
@@ -1517,37 +1586,11 @@ function ChatPanel({
|
|
|
1517
1586
|
enabled: !!effectiveProductBackendUrl && orgConfigState.status === "success"
|
|
1518
1587
|
// Only enable after config is fetched
|
|
1519
1588
|
});
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
const url = `${effectiveProductBackendUrl}/getDocument/snowkite/categories/`;
|
|
1526
|
-
console.log("[KiteChat TEST] Testing product backend connectivity...");
|
|
1527
|
-
console.log("[KiteChat TEST] URL:", url);
|
|
1528
|
-
try {
|
|
1529
|
-
const response = await fetch(url, {
|
|
1530
|
-
method: "GET",
|
|
1531
|
-
// Note: not using credentials: 'include' to avoid CORS issues with wildcard
|
|
1532
|
-
headers: {
|
|
1533
|
-
"Accept": "application/json"
|
|
1534
|
-
}
|
|
1535
|
-
});
|
|
1536
|
-
console.log("[KiteChat TEST] Response status:", response.status);
|
|
1537
|
-
console.log("[KiteChat TEST] Response ok:", response.ok);
|
|
1538
|
-
if (response.ok) {
|
|
1539
|
-
const data = await response.json();
|
|
1540
|
-
console.log("[KiteChat TEST] SUCCESS - product backend reachable, data:", data);
|
|
1541
|
-
} else {
|
|
1542
|
-
const errorText = await response.text();
|
|
1543
|
-
console.log("[KiteChat TEST] FAILED - status:", response.status, "body:", errorText);
|
|
1544
|
-
}
|
|
1545
|
-
} catch (error) {
|
|
1546
|
-
console.error("[KiteChat TEST] ERROR:", error);
|
|
1547
|
-
}
|
|
1548
|
-
};
|
|
1549
|
-
testProductBackendEndpoint();
|
|
1550
|
-
}, [effectiveProductBackendUrl, orgConfigState.status]);
|
|
1589
|
+
const { executeToolRequest } = useFrontendToolExecutor({
|
|
1590
|
+
productBackendUrl: effectiveProductBackendUrl || "",
|
|
1591
|
+
agentUrl,
|
|
1592
|
+
sessionId
|
|
1593
|
+
});
|
|
1551
1594
|
const effectiveUser = React6.useMemo(() => {
|
|
1552
1595
|
if (sessionUser) {
|
|
1553
1596
|
return sessionUser;
|
|
@@ -1578,7 +1621,6 @@ function ChatPanel({
|
|
|
1578
1621
|
userRole: authState.user.role,
|
|
1579
1622
|
isInternal: authState.user.isInternal
|
|
1580
1623
|
});
|
|
1581
|
-
console.log("[ChatPanel] Session user captured:", authState.user.id);
|
|
1582
1624
|
}
|
|
1583
1625
|
}, [authState, sessionUser]);
|
|
1584
1626
|
const isWaitingForAuth = React6.useMemo(() => {
|
|
@@ -1591,26 +1633,18 @@ function ChatPanel({
|
|
|
1591
1633
|
const supabaseRef = React6.useRef(null);
|
|
1592
1634
|
const typingChannelRef = React6.useRef(null);
|
|
1593
1635
|
const typingTimeoutRef = React6.useRef(null);
|
|
1594
|
-
React6.useEffect(() => {
|
|
1595
|
-
console.log(`[KiteChat] Chat Panel v${CHAT_PANEL_VERSION} loaded`);
|
|
1596
|
-
}, []);
|
|
1597
1636
|
const resetSession = React6.useCallback(() => {
|
|
1598
|
-
console.log("[KiteChat] resetSession called", { isEscalated, hasSupabase: !!supabaseRef.current, sessionId });
|
|
1599
1637
|
if (isEscalated && supabaseRef.current && sessionId) {
|
|
1600
|
-
console.log("[KiteChat] Updating customer_status to disconnected for session:", sessionId);
|
|
1601
1638
|
supabaseRef.current.from("escalations").update({
|
|
1602
1639
|
customer_status: "disconnected",
|
|
1603
1640
|
customer_last_seen: (/* @__PURE__ */ new Date()).toISOString()
|
|
1604
1641
|
}).eq("session_id", sessionId).then(
|
|
1605
|
-
(
|
|
1606
|
-
console.log("[KiteChat] Disconnect update result:", result);
|
|
1642
|
+
() => {
|
|
1607
1643
|
},
|
|
1608
1644
|
(err) => {
|
|
1609
1645
|
console.error("[KiteChat] Disconnect update failed:", err);
|
|
1610
1646
|
}
|
|
1611
1647
|
);
|
|
1612
|
-
} else {
|
|
1613
|
-
console.log("[KiteChat] Skipping disconnect update - conditions not met");
|
|
1614
1648
|
}
|
|
1615
1649
|
setSessionUser(null);
|
|
1616
1650
|
setSessionId(crypto.randomUUID());
|
|
@@ -1636,12 +1670,9 @@ function ChatPanel({
|
|
|
1636
1670
|
}
|
|
1637
1671
|
const channelName = `typing:${sessionId}`;
|
|
1638
1672
|
const channel = supabaseRef.current.channel(channelName);
|
|
1639
|
-
console.log(`[KiteChat] Subscribing to typing channel: ${channelName}`);
|
|
1640
1673
|
channel.on("broadcast", { event: "typing" }, (payload) => {
|
|
1641
|
-
console.log("[KiteChat] Received typing broadcast:", payload);
|
|
1642
1674
|
const { sender, isTyping } = payload.payload;
|
|
1643
1675
|
if (sender === "agent") {
|
|
1644
|
-
console.log(`[KiteChat] Agent typing: ${isTyping}`);
|
|
1645
1676
|
setAgentIsTyping(isTyping);
|
|
1646
1677
|
if (isTyping) {
|
|
1647
1678
|
if (typingTimeoutRef.current) {
|
|
@@ -1653,10 +1684,11 @@ function ChatPanel({
|
|
|
1653
1684
|
}
|
|
1654
1685
|
}
|
|
1655
1686
|
}).subscribe((status) => {
|
|
1656
|
-
console.log(`[KiteChat] Typing channel status: ${status}`);
|
|
1657
1687
|
if (status === "SUBSCRIBED") {
|
|
1658
1688
|
typingChannelRef.current = channel;
|
|
1659
|
-
console.log("[KiteChat] Typing channel
|
|
1689
|
+
console.log("[KiteChat] Typing channel subscribed successfully");
|
|
1690
|
+
} else if (status === "CHANNEL_ERROR") {
|
|
1691
|
+
console.error("[KiteChat] Typing channel subscription failed");
|
|
1660
1692
|
}
|
|
1661
1693
|
});
|
|
1662
1694
|
return () => {
|
|
@@ -1667,6 +1699,20 @@ function ChatPanel({
|
|
|
1667
1699
|
}
|
|
1668
1700
|
};
|
|
1669
1701
|
}, [isEscalated, sessionId]);
|
|
1702
|
+
React6.useEffect(() => {
|
|
1703
|
+
if (!isOpen && isEscalated && supabaseRef.current && sessionId) {
|
|
1704
|
+
console.log("[KiteChat] Panel closed during live chat, marking disconnected");
|
|
1705
|
+
supabaseRef.current.from("escalations").update({
|
|
1706
|
+
customer_status: "disconnected",
|
|
1707
|
+
customer_last_seen: (/* @__PURE__ */ new Date()).toISOString()
|
|
1708
|
+
}).eq("session_id", sessionId).then(
|
|
1709
|
+
() => console.log("[KiteChat] Successfully marked disconnected on panel close"),
|
|
1710
|
+
(err) => {
|
|
1711
|
+
console.error("[KiteChat] Failed to mark disconnected on panel close:", err);
|
|
1712
|
+
}
|
|
1713
|
+
);
|
|
1714
|
+
}
|
|
1715
|
+
}, [isOpen, isEscalated, sessionId]);
|
|
1670
1716
|
const heartbeatIntervalRef = React6.useRef(null);
|
|
1671
1717
|
const updateCustomerStatus = React6.useCallback(async (status) => {
|
|
1672
1718
|
if (!supabaseRef.current || !sessionId) return;
|
|
@@ -1685,9 +1731,20 @@ function ChatPanel({
|
|
|
1685
1731
|
}
|
|
1686
1732
|
const currentSessionId = sessionId;
|
|
1687
1733
|
const supabase = supabaseRef.current;
|
|
1688
|
-
|
|
1734
|
+
const markActive = () => {
|
|
1735
|
+
supabase.from("escalations").update({
|
|
1736
|
+
customer_status: "active",
|
|
1737
|
+
customer_last_seen: (/* @__PURE__ */ new Date()).toISOString()
|
|
1738
|
+
}).eq("session_id", currentSessionId).then(
|
|
1739
|
+
() => {
|
|
1740
|
+
},
|
|
1741
|
+
(err) => console.error("[KiteChat] Failed to update customer status:", err)
|
|
1742
|
+
);
|
|
1743
|
+
};
|
|
1744
|
+
console.log("[KiteChat] Starting presence heartbeat for live chat");
|
|
1745
|
+
markActive();
|
|
1689
1746
|
heartbeatIntervalRef.current = window.setInterval(() => {
|
|
1690
|
-
|
|
1747
|
+
markActive();
|
|
1691
1748
|
}, 6e4);
|
|
1692
1749
|
const handleBeforeUnload = () => {
|
|
1693
1750
|
supabase.from("escalations").update({
|
|
@@ -1702,7 +1759,7 @@ function ChatPanel({
|
|
|
1702
1759
|
customer_last_seen: (/* @__PURE__ */ new Date()).toISOString()
|
|
1703
1760
|
}).eq("session_id", currentSessionId);
|
|
1704
1761
|
} else if (document.visibilityState === "visible") {
|
|
1705
|
-
|
|
1762
|
+
markActive();
|
|
1706
1763
|
}
|
|
1707
1764
|
};
|
|
1708
1765
|
window.addEventListener("beforeunload", handleBeforeUnload);
|
|
@@ -1710,13 +1767,12 @@ function ChatPanel({
|
|
|
1710
1767
|
return () => {
|
|
1711
1768
|
window.removeEventListener("beforeunload", handleBeforeUnload);
|
|
1712
1769
|
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
1770
|
+
console.log("[KiteChat] Escalation ended, marking disconnected");
|
|
1713
1771
|
supabase.from("escalations").update({
|
|
1714
1772
|
customer_status: "disconnected",
|
|
1715
1773
|
customer_last_seen: (/* @__PURE__ */ new Date()).toISOString()
|
|
1716
1774
|
}).eq("session_id", currentSessionId).then(
|
|
1717
|
-
() =>
|
|
1718
|
-
console.log("[KiteChat] Marked customer as disconnected");
|
|
1719
|
-
},
|
|
1775
|
+
() => console.log("[KiteChat] Successfully marked disconnected on escalation end"),
|
|
1720
1776
|
(err) => {
|
|
1721
1777
|
console.error("[KiteChat] Failed to mark disconnected:", err);
|
|
1722
1778
|
}
|
|
@@ -1726,17 +1782,14 @@ function ChatPanel({
|
|
|
1726
1782
|
heartbeatIntervalRef.current = null;
|
|
1727
1783
|
}
|
|
1728
1784
|
};
|
|
1729
|
-
}, [isEscalated, sessionId
|
|
1785
|
+
}, [isEscalated, sessionId]);
|
|
1730
1786
|
const sendTypingIndicator = React6.useCallback((isTyping) => {
|
|
1731
|
-
if (!typingChannelRef.current) {
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
if (!isEscalated) {
|
|
1736
|
-
console.log("[KiteChat] Cannot send typing - not escalated");
|
|
1787
|
+
if (!typingChannelRef.current || !isEscalated) {
|
|
1788
|
+
if (isTyping) {
|
|
1789
|
+
console.warn("[KiteChat] Typing channel not ready, cannot send typing indicator");
|
|
1790
|
+
}
|
|
1737
1791
|
return;
|
|
1738
1792
|
}
|
|
1739
|
-
console.log(`[KiteChat] Sending typing indicator: ${isTyping}`);
|
|
1740
1793
|
typingChannelRef.current.send({
|
|
1741
1794
|
type: "broadcast",
|
|
1742
1795
|
event: "typing",
|
|
@@ -1745,16 +1798,23 @@ function ChatPanel({
|
|
|
1745
1798
|
}, [isEscalated]);
|
|
1746
1799
|
const userTypingTimeoutRef = React6.useRef(null);
|
|
1747
1800
|
const handleTypingStart = React6.useCallback(() => {
|
|
1748
|
-
if (!isEscalated || !supabaseRef.current) return;
|
|
1801
|
+
if (!isEscalated || !supabaseRef.current || !sessionId) return;
|
|
1749
1802
|
sendTypingIndicator(true);
|
|
1750
|
-
|
|
1803
|
+
supabaseRef.current.from("escalations").update({
|
|
1804
|
+
customer_status: "active",
|
|
1805
|
+
customer_last_seen: (/* @__PURE__ */ new Date()).toISOString()
|
|
1806
|
+
}).eq("session_id", sessionId).then(
|
|
1807
|
+
() => {
|
|
1808
|
+
},
|
|
1809
|
+
(err) => console.error("[KiteChat] Failed to update presence:", err)
|
|
1810
|
+
);
|
|
1751
1811
|
if (userTypingTimeoutRef.current) {
|
|
1752
1812
|
window.clearTimeout(userTypingTimeoutRef.current);
|
|
1753
1813
|
}
|
|
1754
1814
|
userTypingTimeoutRef.current = window.setTimeout(() => {
|
|
1755
1815
|
sendTypingIndicator(false);
|
|
1756
1816
|
}, 1500);
|
|
1757
|
-
}, [isEscalated, sendTypingIndicator,
|
|
1817
|
+
}, [isEscalated, sendTypingIndicator, sessionId]);
|
|
1758
1818
|
const streamIntervals = React6.useRef({});
|
|
1759
1819
|
const isEmpty = messages.length === 0;
|
|
1760
1820
|
const [phase, setPhase] = React6.useState("idle");
|
|
@@ -1789,12 +1849,6 @@ function ChatPanel({
|
|
|
1789
1849
|
const latestBulkSummaryNavigationRef = React6.useRef(null);
|
|
1790
1850
|
const [guideComplete, setGuideComplete] = React6.useState(false);
|
|
1791
1851
|
React6.useEffect(() => {
|
|
1792
|
-
window.resetIntegrationNotification = () => {
|
|
1793
|
-
localStorage.removeItem("gmailNotificationSeen");
|
|
1794
|
-
console.log(
|
|
1795
|
-
"Integration notification reset! Click the Integrations tab to see it again."
|
|
1796
|
-
);
|
|
1797
|
-
};
|
|
1798
1852
|
const handleIntegrationTabClick = () => {
|
|
1799
1853
|
const hasSeenNotification = localStorage.getItem("gmailNotificationSeen");
|
|
1800
1854
|
if (!hasSeenNotification) {
|
|
@@ -1936,17 +1990,7 @@ function ChatPanel({
|
|
|
1936
1990
|
return;
|
|
1937
1991
|
}
|
|
1938
1992
|
const currentBulkNav = latestBulkSummaryNavigationRef.current;
|
|
1939
|
-
console.log(
|
|
1940
|
-
"[DEBUG] Keyboard handler - latestBulkSummaryNavigation:",
|
|
1941
|
-
currentBulkNav,
|
|
1942
|
-
"onNavigate:",
|
|
1943
|
-
!!onNavigate
|
|
1944
|
-
);
|
|
1945
1993
|
if (currentBulkNav && onNavigate) {
|
|
1946
|
-
console.log(
|
|
1947
|
-
"[DEBUG] Navigating via keyboard to:",
|
|
1948
|
-
currentBulkNav.page
|
|
1949
|
-
);
|
|
1950
1994
|
e.preventDefault();
|
|
1951
1995
|
e.stopPropagation();
|
|
1952
1996
|
onNavigate(currentBulkNav.page, currentBulkNav.subtab);
|
|
@@ -1992,7 +2036,6 @@ function ChatPanel({
|
|
|
1992
2036
|
const messageId = data.message_id;
|
|
1993
2037
|
const isDuplicate = messageId ? prev.some((m) => m.serverMessageId === messageId) : prev.slice(-5).some((m) => m.content === content);
|
|
1994
2038
|
if (isDuplicate) {
|
|
1995
|
-
console.debug("[KiteChat] Skipping duplicate agent message:", messageId || content.slice(0, 30));
|
|
1996
2039
|
return prev;
|
|
1997
2040
|
}
|
|
1998
2041
|
return [...prev, {
|
|
@@ -2015,7 +2058,6 @@ function ChatPanel({
|
|
|
2015
2058
|
console.error("[KiteChat] Escalation WebSocket error:", err);
|
|
2016
2059
|
};
|
|
2017
2060
|
ws.onclose = () => {
|
|
2018
|
-
console.log("[KiteChat] Escalation WebSocket closed");
|
|
2019
2061
|
};
|
|
2020
2062
|
escalationWsRef.current = ws;
|
|
2021
2063
|
}, [agentUrl]);
|
|
@@ -2029,13 +2071,22 @@ function ChatPanel({
|
|
|
2029
2071
|
type: "user_message",
|
|
2030
2072
|
content
|
|
2031
2073
|
}));
|
|
2032
|
-
|
|
2074
|
+
if (supabaseRef.current && sessionId) {
|
|
2075
|
+
supabaseRef.current.from("escalations").update({
|
|
2076
|
+
customer_status: "active",
|
|
2077
|
+
customer_last_seen: (/* @__PURE__ */ new Date()).toISOString()
|
|
2078
|
+
}).eq("session_id", sessionId).then(
|
|
2079
|
+
() => {
|
|
2080
|
+
},
|
|
2081
|
+
(err) => console.error("[KiteChat] Failed to update presence:", err)
|
|
2082
|
+
);
|
|
2083
|
+
}
|
|
2033
2084
|
return true;
|
|
2034
2085
|
} catch (err) {
|
|
2035
2086
|
console.error("[KiteChat] Failed to send escalated message:", err);
|
|
2036
2087
|
return false;
|
|
2037
2088
|
}
|
|
2038
|
-
}, [
|
|
2089
|
+
}, [sessionId]);
|
|
2039
2090
|
React6.useEffect(() => {
|
|
2040
2091
|
return () => {
|
|
2041
2092
|
if (escalationWsRef.current) {
|
|
@@ -2309,14 +2360,6 @@ function ChatPanel({
|
|
|
2309
2360
|
try {
|
|
2310
2361
|
const controller = new AbortController();
|
|
2311
2362
|
const timeoutId = setTimeout(() => controller.abort(), 6e4);
|
|
2312
|
-
console.log("[ChatPanel] Sending chat request to agent backend...");
|
|
2313
|
-
console.log("[ChatPanel] Agent URL:", agentUrl);
|
|
2314
|
-
console.log("[ChatPanel] User data being sent:");
|
|
2315
|
-
console.log("[ChatPanel] user_id:", effectiveUser.userId);
|
|
2316
|
-
console.log("[ChatPanel] user_name:", effectiveUser.userName);
|
|
2317
|
-
console.log("[ChatPanel] user_email:", effectiveUser.userEmail);
|
|
2318
|
-
console.log("[ChatPanel] org_id:", orgId);
|
|
2319
|
-
console.log("[ChatPanel] authState.status:", authState.status);
|
|
2320
2363
|
const response = await fetch(`${agentUrl}/chat/stream`, {
|
|
2321
2364
|
method: "POST",
|
|
2322
2365
|
headers: {
|
|
@@ -2531,6 +2574,11 @@ function ChatPanel({
|
|
|
2531
2574
|
content: data.message || "You've been connected to our support queue. An agent will be with you shortly."
|
|
2532
2575
|
};
|
|
2533
2576
|
setMessages((prev) => [...prev, escalationMessage]);
|
|
2577
|
+
} else if (eventType === "tool_request") {
|
|
2578
|
+
const toolRequest = data;
|
|
2579
|
+
executeToolRequest(toolRequest).catch((err) => {
|
|
2580
|
+
console.error("[KiteChat] Tool execution failed:", err);
|
|
2581
|
+
});
|
|
2534
2582
|
} else if (eventType === "token") {
|
|
2535
2583
|
}
|
|
2536
2584
|
} catch (parseError) {
|
|
@@ -2845,11 +2893,6 @@ ${userText}`
|
|
|
2845
2893
|
}
|
|
2846
2894
|
});
|
|
2847
2895
|
} else if (eventType === "summary") {
|
|
2848
|
-
console.log("[DEBUG] Received summary event - data:", data);
|
|
2849
|
-
console.log(
|
|
2850
|
-
"[DEBUG] navigationPage from backend:",
|
|
2851
|
-
data.navigationPage
|
|
2852
|
-
);
|
|
2853
2896
|
setPhase("idle");
|
|
2854
2897
|
setProgressSteps([]);
|
|
2855
2898
|
setPendingBulkSession(null);
|
|
@@ -2869,7 +2912,6 @@ ${userText}`
|
|
|
2869
2912
|
navigationPage: data.navigationPage
|
|
2870
2913
|
}
|
|
2871
2914
|
};
|
|
2872
|
-
console.log("[DEBUG] Creating bulkSummary message:", newMsg);
|
|
2873
2915
|
return [...filtered, newMsg];
|
|
2874
2916
|
});
|
|
2875
2917
|
setTimeout(() => {
|
|
@@ -4440,28 +4482,11 @@ ${userText}`
|
|
|
4440
4482
|
onClick: (e) => {
|
|
4441
4483
|
e.preventDefault();
|
|
4442
4484
|
e.stopPropagation();
|
|
4443
|
-
console.log(
|
|
4444
|
-
"[DEBUG] Button clicked - navigationPage:",
|
|
4445
|
-
navigationPage,
|
|
4446
|
-
"onNavigate:",
|
|
4447
|
-
!!onNavigate
|
|
4448
|
-
);
|
|
4449
4485
|
if (onNavigate && navigationPage.page) {
|
|
4450
|
-
console.log(
|
|
4451
|
-
"[DEBUG] Calling onNavigate with page:",
|
|
4452
|
-
navigationPage.page
|
|
4453
|
-
);
|
|
4454
4486
|
onNavigate(
|
|
4455
4487
|
navigationPage.page,
|
|
4456
4488
|
navigationPage.subtab
|
|
4457
4489
|
);
|
|
4458
|
-
} else {
|
|
4459
|
-
console.log(
|
|
4460
|
-
"[DEBUG] Condition failed - onNavigate:",
|
|
4461
|
-
!!onNavigate,
|
|
4462
|
-
"navigationPage.page:",
|
|
4463
|
-
navigationPage.page
|
|
4464
|
-
);
|
|
4465
4490
|
}
|
|
4466
4491
|
},
|
|
4467
4492
|
className: "flex items-center gap-2 text-xs text-gray-500 hover:text-gray-700 transition-colors group cursor-pointer",
|
|
@@ -4741,7 +4766,8 @@ function ChatPanelWithToggle({
|
|
|
4741
4766
|
supabaseAnonKey,
|
|
4742
4767
|
initialCorner,
|
|
4743
4768
|
onCornerChange,
|
|
4744
|
-
productBackendUrl
|
|
4769
|
+
productBackendUrl,
|
|
4770
|
+
getAuthHeaders
|
|
4745
4771
|
}) {
|
|
4746
4772
|
const [internalIsOpen, setInternalIsOpen] = React6.useState(defaultOpen);
|
|
4747
4773
|
const isOpen = controlledIsOpen !== void 0 ? controlledIsOpen : internalIsOpen;
|
|
@@ -4771,7 +4797,8 @@ function ChatPanelWithToggle({
|
|
|
4771
4797
|
supabaseAnonKey,
|
|
4772
4798
|
initialCorner,
|
|
4773
4799
|
onCornerChange,
|
|
4774
|
-
productBackendUrl
|
|
4800
|
+
productBackendUrl,
|
|
4801
|
+
getAuthHeaders
|
|
4775
4802
|
}
|
|
4776
4803
|
);
|
|
4777
4804
|
}
|
|
@@ -4845,7 +4872,8 @@ function KiteChatWrapper({
|
|
|
4845
4872
|
userEmail: config.userEmail,
|
|
4846
4873
|
supabaseUrl: config.supabaseUrl,
|
|
4847
4874
|
supabaseAnonKey: config.supabaseAnonKey,
|
|
4848
|
-
productBackendUrl: config.productBackendUrl
|
|
4875
|
+
productBackendUrl: config.productBackendUrl,
|
|
4876
|
+
getAuthHeaders: config.getAuthHeaders
|
|
4849
4877
|
}
|
|
4850
4878
|
);
|
|
4851
4879
|
}
|
|
@@ -4888,7 +4916,6 @@ function createKiteChat(config) {
|
|
|
4888
4916
|
}
|
|
4889
4917
|
)
|
|
4890
4918
|
);
|
|
4891
|
-
console.log("[KiteChat] Mounted");
|
|
4892
4919
|
},
|
|
4893
4920
|
unmount() {
|
|
4894
4921
|
if (!root) {
|
|
@@ -4900,7 +4927,6 @@ function createKiteChat(config) {
|
|
|
4900
4927
|
containerElement = null;
|
|
4901
4928
|
configUpdater = null;
|
|
4902
4929
|
stateUpdaters = null;
|
|
4903
|
-
console.log("[KiteChat] Unmounted");
|
|
4904
4930
|
},
|
|
4905
4931
|
open() {
|
|
4906
4932
|
stateUpdaters?.setIsOpen(true);
|
|
@@ -118,8 +118,10 @@ interface ChatPanelProps {
|
|
|
118
118
|
onCornerChange?: (corner: PanelCorner) => void;
|
|
119
119
|
/** Product backend URL for user authentication (e.g., https://dev.api.rocketalumnisolutions.com) */
|
|
120
120
|
productBackendUrl?: string;
|
|
121
|
+
/** Optional async function to provide authentication headers for API requests */
|
|
122
|
+
getAuthHeaders?: () => Promise<Record<string, string>>;
|
|
121
123
|
}
|
|
122
|
-
declare function ChatPanel({ isOpen, onClose, onOpen, onBack, onNavigate, onActionComplete, currentPage, agentUrl, startingQuestions: startingQuestionsProp, startingQuestionsEndpoint, userId, orgId, userName, userEmail, supabaseUrl, supabaseAnonKey, initialCorner, onCornerChange, productBackendUrl, }?: ChatPanelProps): react_jsx_runtime.JSX.Element;
|
|
124
|
+
declare function ChatPanel({ isOpen, onClose, onOpen, onBack, onNavigate, onActionComplete, currentPage, agentUrl, startingQuestions: startingQuestionsProp, startingQuestionsEndpoint, userId, orgId, userName, userEmail, supabaseUrl, supabaseAnonKey, initialCorner, onCornerChange, productBackendUrl, getAuthHeaders, }?: ChatPanelProps): react_jsx_runtime.JSX.Element;
|
|
123
125
|
/**
|
|
124
126
|
* PanelToggle - An arrow button on the right edge that toggles the side panel
|
|
125
127
|
* Shows left arrow when closed (click to open), right arrow when open (click to close)
|
|
@@ -182,8 +184,10 @@ interface ChatPanelWithToggleProps {
|
|
|
182
184
|
onCornerChange?: (corner: PanelCorner) => void;
|
|
183
185
|
/** Product backend URL for user authentication (e.g., https://dev.api.rocketalumnisolutions.com) */
|
|
184
186
|
productBackendUrl?: string;
|
|
187
|
+
/** Optional async function to provide authentication headers for API requests */
|
|
188
|
+
getAuthHeaders?: () => Promise<Record<string, string>>;
|
|
185
189
|
}
|
|
186
|
-
declare function ChatPanelWithToggle({ onNavigate, onActionComplete, currentPage, agentUrl, startingQuestions, startingQuestionsEndpoint, defaultOpen, isOpen: controlledIsOpen, onOpenChange, userId, orgId, userName, userEmail, supabaseUrl, supabaseAnonKey, initialCorner, onCornerChange, productBackendUrl, }: ChatPanelWithToggleProps): react_jsx_runtime.JSX.Element;
|
|
190
|
+
declare function ChatPanelWithToggle({ onNavigate, onActionComplete, currentPage, agentUrl, startingQuestions, startingQuestionsEndpoint, defaultOpen, isOpen: controlledIsOpen, onOpenChange, userId, orgId, userName, userEmail, supabaseUrl, supabaseAnonKey, initialCorner, onCornerChange, productBackendUrl, getAuthHeaders, }: ChatPanelWithToggleProps): react_jsx_runtime.JSX.Element;
|
|
187
191
|
/**
|
|
188
192
|
* @deprecated Use ChatPanelWithToggle instead for the new side panel UX
|
|
189
193
|
*/
|
|
@@ -253,6 +257,8 @@ interface KiteChatConfig {
|
|
|
253
257
|
supabaseAnonKey?: string;
|
|
254
258
|
/** Product backend URL for user authentication (e.g., https://dev.api.rocketalumnisolutions.com) */
|
|
255
259
|
productBackendUrl?: string;
|
|
260
|
+
/** Optional async function to provide authentication headers for API requests */
|
|
261
|
+
getAuthHeaders?: () => Promise<Record<string, string>>;
|
|
256
262
|
}
|
|
257
263
|
/**
|
|
258
264
|
* Instance returned by createKiteChat with lifecycle control methods.
|
|
@@ -118,8 +118,10 @@ interface ChatPanelProps {
|
|
|
118
118
|
onCornerChange?: (corner: PanelCorner) => void;
|
|
119
119
|
/** Product backend URL for user authentication (e.g., https://dev.api.rocketalumnisolutions.com) */
|
|
120
120
|
productBackendUrl?: string;
|
|
121
|
+
/** Optional async function to provide authentication headers for API requests */
|
|
122
|
+
getAuthHeaders?: () => Promise<Record<string, string>>;
|
|
121
123
|
}
|
|
122
|
-
declare function ChatPanel({ isOpen, onClose, onOpen, onBack, onNavigate, onActionComplete, currentPage, agentUrl, startingQuestions: startingQuestionsProp, startingQuestionsEndpoint, userId, orgId, userName, userEmail, supabaseUrl, supabaseAnonKey, initialCorner, onCornerChange, productBackendUrl, }?: ChatPanelProps): react_jsx_runtime.JSX.Element;
|
|
124
|
+
declare function ChatPanel({ isOpen, onClose, onOpen, onBack, onNavigate, onActionComplete, currentPage, agentUrl, startingQuestions: startingQuestionsProp, startingQuestionsEndpoint, userId, orgId, userName, userEmail, supabaseUrl, supabaseAnonKey, initialCorner, onCornerChange, productBackendUrl, getAuthHeaders, }?: ChatPanelProps): react_jsx_runtime.JSX.Element;
|
|
123
125
|
/**
|
|
124
126
|
* PanelToggle - An arrow button on the right edge that toggles the side panel
|
|
125
127
|
* Shows left arrow when closed (click to open), right arrow when open (click to close)
|
|
@@ -182,8 +184,10 @@ interface ChatPanelWithToggleProps {
|
|
|
182
184
|
onCornerChange?: (corner: PanelCorner) => void;
|
|
183
185
|
/** Product backend URL for user authentication (e.g., https://dev.api.rocketalumnisolutions.com) */
|
|
184
186
|
productBackendUrl?: string;
|
|
187
|
+
/** Optional async function to provide authentication headers for API requests */
|
|
188
|
+
getAuthHeaders?: () => Promise<Record<string, string>>;
|
|
185
189
|
}
|
|
186
|
-
declare function ChatPanelWithToggle({ onNavigate, onActionComplete, currentPage, agentUrl, startingQuestions, startingQuestionsEndpoint, defaultOpen, isOpen: controlledIsOpen, onOpenChange, userId, orgId, userName, userEmail, supabaseUrl, supabaseAnonKey, initialCorner, onCornerChange, productBackendUrl, }: ChatPanelWithToggleProps): react_jsx_runtime.JSX.Element;
|
|
190
|
+
declare function ChatPanelWithToggle({ onNavigate, onActionComplete, currentPage, agentUrl, startingQuestions, startingQuestionsEndpoint, defaultOpen, isOpen: controlledIsOpen, onOpenChange, userId, orgId, userName, userEmail, supabaseUrl, supabaseAnonKey, initialCorner, onCornerChange, productBackendUrl, getAuthHeaders, }: ChatPanelWithToggleProps): react_jsx_runtime.JSX.Element;
|
|
187
191
|
/**
|
|
188
192
|
* @deprecated Use ChatPanelWithToggle instead for the new side panel UX
|
|
189
193
|
*/
|
|
@@ -253,6 +257,8 @@ interface KiteChatConfig {
|
|
|
253
257
|
supabaseAnonKey?: string;
|
|
254
258
|
/** Product backend URL for user authentication (e.g., https://dev.api.rocketalumnisolutions.com) */
|
|
255
259
|
productBackendUrl?: string;
|
|
260
|
+
/** Optional async function to provide authentication headers for API requests */
|
|
261
|
+
getAuthHeaders?: () => Promise<Record<string, string>>;
|
|
256
262
|
}
|
|
257
263
|
/**
|
|
258
264
|
* Instance returned by createKiteChat with lifecycle control methods.
|