@kite-copilot/chat-panel 0.2.48 → 0.2.49

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 CHANGED
@@ -548,6 +548,36 @@ If upgrading from a previous version with overlay/floating panel:
548
548
  <ChatPanelWithToggle />
549
549
  ```
550
550
 
551
+ ## Local Development with Test Page
552
+
553
+ To test the embed script locally using the included test page:
554
+
555
+ 1. **Install dependencies:**
556
+ ```bash
557
+ npm install
558
+ ```
559
+
560
+ 2. **Build the embed script:**
561
+ ```bash
562
+ npm run build
563
+ ```
564
+
565
+ 3. **Serve the test page:**
566
+ ```bash
567
+ npx serve .
568
+ ```
569
+
570
+ 4. **Open in browser:**
571
+ Navigate to `http://localhost:3000/test/test-embed.html`
572
+
573
+ The test page (`test/test-embed.html`) provides:
574
+ - Interactive controls to open/close/toggle the panel
575
+ - Page context switching buttons
576
+ - Backend URL toggle (localhost vs production)
577
+ - Event logging for navigation and action callbacks
578
+
579
+ **Note:** Make sure your backend agent is running on `http://localhost:5002` (or toggle to production in the test page).
580
+
551
581
  ## License
552
582
 
553
583
  MIT © Kite
package/dist/auto.cjs CHANGED
@@ -35,7 +35,7 @@ __export(auto_exports, {
35
35
  module.exports = __toCommonJS(auto_exports);
36
36
 
37
37
  // src/createKiteChat.tsx
38
- var import_react = __toESM(require("react"), 1);
38
+ var import_react2 = __toESM(require("react"), 1);
39
39
  var import_client = require("react-dom/client");
40
40
 
41
41
  // src/ChatPanel.tsx
@@ -447,6 +447,78 @@ function useOrgConfig({ agentUrl, orgId }) {
447
447
  return state;
448
448
  }
449
449
 
450
+ // src/hooks/useFrontendToolExecutor.ts
451
+ var import_react = require("react");
452
+ function useFrontendToolExecutor({
453
+ productBackendUrl,
454
+ agentUrl,
455
+ sessionId
456
+ }) {
457
+ const sendToolResult = (0, import_react.useCallback)(async (payload) => {
458
+ try {
459
+ await fetch(`${agentUrl}/chat/tool-result`, {
460
+ method: "POST",
461
+ headers: {
462
+ "Content-Type": "application/json"
463
+ },
464
+ body: JSON.stringify(payload)
465
+ });
466
+ } catch (error) {
467
+ console.error("[FrontendToolExecutor] Failed to send tool result:", error);
468
+ }
469
+ }, [agentUrl]);
470
+ const executeToolRequest = (0, import_react.useCallback)(async (toolRequest) => {
471
+ const { call_id, tool_name, arguments: args, endpoint, method, path_params } = toolRequest;
472
+ console.log("[FrontendToolExecutor] Executing tool:", tool_name, "with args:", args);
473
+ try {
474
+ let url = endpoint;
475
+ for (const param of path_params) {
476
+ if (args[param]) {
477
+ url = url.replace(`{${param}}`, encodeURIComponent(args[param]));
478
+ }
479
+ }
480
+ const queryParams = new URLSearchParams();
481
+ for (const [key, value] of Object.entries(args)) {
482
+ if (!path_params.includes(key) && value !== void 0 && value !== null) {
483
+ queryParams.append(key, String(value));
484
+ }
485
+ }
486
+ const queryString = queryParams.toString();
487
+ const fullUrl = `${productBackendUrl}${url}${queryString ? "?" + queryString : ""}`;
488
+ console.log("[FrontendToolExecutor] Fetching:", fullUrl);
489
+ const response = await fetch(fullUrl, {
490
+ method,
491
+ credentials: "include",
492
+ headers: {
493
+ "Accept": "application/json",
494
+ "Content-Type": "application/json"
495
+ }
496
+ });
497
+ let result;
498
+ if (response.ok) {
499
+ result = await response.json();
500
+ console.log("[FrontendToolExecutor] Tool result:", result);
501
+ } else {
502
+ const errorText = await response.text();
503
+ throw new Error(`HTTP ${response.status}: ${errorText}`);
504
+ }
505
+ await sendToolResult({
506
+ session_id: sessionId,
507
+ call_id,
508
+ result
509
+ });
510
+ } catch (error) {
511
+ console.error("[FrontendToolExecutor] Tool execution failed:", error);
512
+ await sendToolResult({
513
+ session_id: sessionId,
514
+ call_id,
515
+ error: error instanceof Error ? error.message : "Unknown error"
516
+ });
517
+ }
518
+ }, [productBackendUrl, sessionId, sendToolResult]);
519
+ return { executeToolRequest };
520
+ }
521
+
450
522
  // src/components/ui/card.tsx
451
523
  var import_jsx_runtime7 = require("react/jsx-runtime");
452
524
  function Card({ className, ...props }) {
@@ -1024,7 +1096,7 @@ function TypingIndicator({ className = "" }) {
1024
1096
 
1025
1097
  // src/ChatPanel.tsx
1026
1098
  var import_jsx_runtime10 = require("react/jsx-runtime");
1027
- var CHAT_PANEL_VERSION = true ? "0.2.48" : "dev";
1099
+ var CHAT_PANEL_VERSION = true ? "0.2.49" : "dev";
1028
1100
  var DEFAULT_AGENT_URL = "http://localhost:5002";
1029
1101
  var PANEL_WIDTH = 400;
1030
1102
  var PANEL_HEIGHT = 600;
@@ -1516,6 +1588,11 @@ function ChatPanel({
1516
1588
  enabled: !!effectiveProductBackendUrl && orgConfigState.status === "success"
1517
1589
  // Only enable after config is fetched
1518
1590
  });
1591
+ const { executeToolRequest } = useFrontendToolExecutor({
1592
+ productBackendUrl: effectiveProductBackendUrl || "",
1593
+ agentUrl,
1594
+ sessionId
1595
+ });
1519
1596
  React6.useEffect(() => {
1520
1597
  if (!effectiveProductBackendUrl || orgConfigState.status !== "success") {
1521
1598
  return;
@@ -2530,6 +2607,12 @@ function ChatPanel({
2530
2607
  content: data.message || "You've been connected to our support queue. An agent will be with you shortly."
2531
2608
  };
2532
2609
  setMessages((prev) => [...prev, escalationMessage]);
2610
+ } else if (eventType === "tool_request") {
2611
+ const toolRequest = data;
2612
+ console.log("[KiteChat] Received tool_request:", toolRequest);
2613
+ executeToolRequest(toolRequest).catch((err) => {
2614
+ console.error("[KiteChat] Tool execution failed:", err);
2615
+ });
2533
2616
  } else if (eventType === "token") {
2534
2617
  }
2535
2618
  } catch (parseError) {
@@ -4762,14 +4845,14 @@ function KiteChatWrapper({
4762
4845
  onConfigUpdate,
4763
4846
  onStateUpdate
4764
4847
  }) {
4765
- const [config, setConfig] = import_react.default.useState(initialConfig);
4766
- const [currentPage, setCurrentPage] = import_react.default.useState(initialConfig.currentPage || "dashboard");
4767
- const [isOpen, setIsOpen] = import_react.default.useState(false);
4768
- const isOpenRef = import_react.default.useRef(false);
4769
- import_react.default.useEffect(() => {
4848
+ const [config, setConfig] = import_react2.default.useState(initialConfig);
4849
+ const [currentPage, setCurrentPage] = import_react2.default.useState(initialConfig.currentPage || "dashboard");
4850
+ const [isOpen, setIsOpen] = import_react2.default.useState(false);
4851
+ const isOpenRef = import_react2.default.useRef(false);
4852
+ import_react2.default.useEffect(() => {
4770
4853
  isOpenRef.current = isOpen;
4771
4854
  }, [isOpen]);
4772
- import_react.default.useEffect(() => {
4855
+ import_react2.default.useEffect(() => {
4773
4856
  onConfigUpdate((newConfig) => {
4774
4857
  if (newConfig.currentPage !== void 0) {
4775
4858
  setCurrentPage(newConfig.currentPage);
@@ -4781,7 +4864,7 @@ function KiteChatWrapper({
4781
4864
  getIsOpen: () => isOpenRef.current
4782
4865
  });
4783
4866
  }, [onConfigUpdate, onStateUpdate]);
4784
- import_react.default.useEffect(() => {
4867
+ import_react2.default.useEffect(() => {
4785
4868
  const container = document.getElementById("kite-chat-root");
4786
4869
  if (!container) return;
4787
4870
  if (config.theme === "dark") {
package/dist/auto.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createKiteChat
3
- } from "./chunk-LOTJ3U5L.js";
3
+ } from "./chunk-YZXB3LLU.js";
4
4
 
5
5
  // src/auto.ts
6
6
  function mountKiteChat(config) {
@@ -995,6 +995,78 @@ 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, tool_name, arguments: args, endpoint, method, path_params } = toolRequest;
1020
+ console.log("[FrontendToolExecutor] Executing tool:", tool_name, "with args:", args);
1021
+ try {
1022
+ let url = endpoint;
1023
+ for (const param of path_params) {
1024
+ if (args[param]) {
1025
+ url = url.replace(`{${param}}`, encodeURIComponent(args[param]));
1026
+ }
1027
+ }
1028
+ const queryParams = new URLSearchParams();
1029
+ for (const [key, value] of Object.entries(args)) {
1030
+ if (!path_params.includes(key) && value !== void 0 && value !== null) {
1031
+ queryParams.append(key, String(value));
1032
+ }
1033
+ }
1034
+ const queryString = queryParams.toString();
1035
+ const fullUrl = `${productBackendUrl}${url}${queryString ? "?" + queryString : ""}`;
1036
+ console.log("[FrontendToolExecutor] Fetching:", fullUrl);
1037
+ const response = await fetch(fullUrl, {
1038
+ method,
1039
+ credentials: "include",
1040
+ headers: {
1041
+ "Accept": "application/json",
1042
+ "Content-Type": "application/json"
1043
+ }
1044
+ });
1045
+ let result;
1046
+ if (response.ok) {
1047
+ result = await response.json();
1048
+ console.log("[FrontendToolExecutor] Tool result:", result);
1049
+ } else {
1050
+ const errorText = await response.text();
1051
+ throw new Error(`HTTP ${response.status}: ${errorText}`);
1052
+ }
1053
+ await sendToolResult({
1054
+ session_id: sessionId,
1055
+ call_id,
1056
+ result
1057
+ });
1058
+ } catch (error) {
1059
+ console.error("[FrontendToolExecutor] Tool execution failed:", error);
1060
+ await sendToolResult({
1061
+ session_id: sessionId,
1062
+ call_id,
1063
+ error: error instanceof Error ? error.message : "Unknown error"
1064
+ });
1065
+ }
1066
+ }, [productBackendUrl, sessionId, sendToolResult]);
1067
+ return { executeToolRequest };
1068
+ }
1069
+
998
1070
  // src/components/TypingIndicator.tsx
999
1071
  import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
1000
1072
  function TypingIndicator({ className = "" }) {
@@ -1025,7 +1097,7 @@ function TypingIndicator({ className = "" }) {
1025
1097
 
1026
1098
  // src/ChatPanel.tsx
1027
1099
  import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
1028
- var CHAT_PANEL_VERSION = true ? "0.2.48" : "dev";
1100
+ var CHAT_PANEL_VERSION = true ? "0.2.49" : "dev";
1029
1101
  var DEFAULT_AGENT_URL = "http://localhost:5002";
1030
1102
  var PANEL_WIDTH = 400;
1031
1103
  var PANEL_HEIGHT = 600;
@@ -1517,6 +1589,11 @@ function ChatPanel({
1517
1589
  enabled: !!effectiveProductBackendUrl && orgConfigState.status === "success"
1518
1590
  // Only enable after config is fetched
1519
1591
  });
1592
+ const { executeToolRequest } = useFrontendToolExecutor({
1593
+ productBackendUrl: effectiveProductBackendUrl || "",
1594
+ agentUrl,
1595
+ sessionId
1596
+ });
1520
1597
  React6.useEffect(() => {
1521
1598
  if (!effectiveProductBackendUrl || orgConfigState.status !== "success") {
1522
1599
  return;
@@ -2531,6 +2608,12 @@ function ChatPanel({
2531
2608
  content: data.message || "You've been connected to our support queue. An agent will be with you shortly."
2532
2609
  };
2533
2610
  setMessages((prev) => [...prev, escalationMessage]);
2611
+ } else if (eventType === "tool_request") {
2612
+ const toolRequest = data;
2613
+ console.log("[KiteChat] Received tool_request:", toolRequest);
2614
+ executeToolRequest(toolRequest).catch((err) => {
2615
+ console.error("[KiteChat] Tool execution failed:", err);
2616
+ });
2534
2617
  } else if (eventType === "token") {
2535
2618
  }
2536
2619
  } catch (parseError) {