@hef2024/llmasaservice-ui 0.24.2 → 0.24.4

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/index.js CHANGED
@@ -693,6 +693,302 @@ var ThinkingBlock = ({
693
693
  );
694
694
  };
695
695
 
696
+ // src/mcpAuth.ts
697
+ function normalizeMcpHeaders(value) {
698
+ const normalized = {};
699
+ if (!value) return normalized;
700
+ for (const [key, raw] of Object.entries(value)) {
701
+ if (typeof raw !== "string") continue;
702
+ const trimmedValue = raw.trim();
703
+ if (!trimmedValue) continue;
704
+ normalized[key] = trimmedValue;
705
+ }
706
+ return normalized;
707
+ }
708
+
709
+ // src/toolArgsParser.ts
710
+ var TOOL_ARGS_CANDIDATE_PARSE_DEPTH = 3;
711
+ var isPlainObject = (value) => {
712
+ return typeof value === "object" && value !== null && !Array.isArray(value);
713
+ };
714
+ var stripMarkdownCodeFence = (input) => {
715
+ const trimmed = input.trim();
716
+ if (!trimmed.startsWith("```")) return trimmed;
717
+ const withoutStart = trimmed.replace(/^```[a-zA-Z0-9_-]*\s*/u, "");
718
+ return withoutStart.replace(/```$/u, "").trim();
719
+ };
720
+ var stripOuterQuotes = (input) => {
721
+ const trimmed = input.trim();
722
+ if (trimmed.length < 2) return trimmed;
723
+ const first = trimmed[0];
724
+ const last = trimmed[trimmed.length - 1];
725
+ if (first === '"' && last === '"' || first === "'" && last === "'") {
726
+ return trimmed.slice(1, -1).trim();
727
+ }
728
+ return trimmed;
729
+ };
730
+ var normalizeSmartQuotes = (input) => {
731
+ return input.replace(/[“”]/gu, '"').replace(/[‘’]/gu, "'");
732
+ };
733
+ var convertSingleQuotedStrings = (input) => {
734
+ let output = "";
735
+ let inSingle = false;
736
+ let inDouble = false;
737
+ let escaped = false;
738
+ for (let index = 0; index < input.length; index += 1) {
739
+ const char = input[index];
740
+ if (inSingle) {
741
+ if (escaped) {
742
+ output += char === '"' ? '\\"' : char;
743
+ escaped = false;
744
+ continue;
745
+ }
746
+ if (char === "\\") {
747
+ output += "\\";
748
+ escaped = true;
749
+ continue;
750
+ }
751
+ if (char === "'") {
752
+ output += '"';
753
+ inSingle = false;
754
+ continue;
755
+ }
756
+ output += char === '"' ? '\\"' : char;
757
+ continue;
758
+ }
759
+ if (inDouble) {
760
+ output += char;
761
+ if (escaped) {
762
+ escaped = false;
763
+ continue;
764
+ }
765
+ if (char === "\\") {
766
+ escaped = true;
767
+ continue;
768
+ }
769
+ if (char === '"') {
770
+ inDouble = false;
771
+ }
772
+ continue;
773
+ }
774
+ if (char === "'") {
775
+ output += '"';
776
+ inSingle = true;
777
+ continue;
778
+ }
779
+ if (char === '"') {
780
+ output += '"';
781
+ inDouble = true;
782
+ continue;
783
+ }
784
+ output += char;
785
+ }
786
+ return output;
787
+ };
788
+ var quoteBareObjectKeys = (input) => {
789
+ return input.replace(
790
+ /([{,]\s*)([A-Za-z_$][A-Za-z0-9_$-]*)(\s*:)/gu,
791
+ '$1"$2"$3'
792
+ );
793
+ };
794
+ var stripTrailingCommas = (input) => {
795
+ return input.replace(/,\s*([}\]])/gu, "$1");
796
+ };
797
+ var normalizeEscapesOutsideStrings = (input) => {
798
+ let output = "";
799
+ let inDouble = false;
800
+ let inSingle = false;
801
+ let escaped = false;
802
+ for (let index = 0; index < input.length; index += 1) {
803
+ const char = input[index];
804
+ const next = index + 1 < input.length ? input[index + 1] : "";
805
+ if (inDouble || inSingle) {
806
+ output += char;
807
+ if (escaped) {
808
+ escaped = false;
809
+ continue;
810
+ }
811
+ if (char === "\\") {
812
+ escaped = true;
813
+ continue;
814
+ }
815
+ if (inDouble && char === '"') {
816
+ inDouble = false;
817
+ } else if (inSingle && char === "'") {
818
+ inSingle = false;
819
+ }
820
+ continue;
821
+ }
822
+ if (char === '"') {
823
+ inDouble = true;
824
+ output += char;
825
+ continue;
826
+ }
827
+ if (char === "'") {
828
+ inSingle = true;
829
+ output += char;
830
+ continue;
831
+ }
832
+ if (char === "\\") {
833
+ if (next === "n") {
834
+ output += "\n";
835
+ index += 1;
836
+ continue;
837
+ }
838
+ if (next === "r") {
839
+ output += "\r";
840
+ index += 1;
841
+ continue;
842
+ }
843
+ if (next === "t") {
844
+ output += " ";
845
+ index += 1;
846
+ continue;
847
+ }
848
+ if (next === '"') {
849
+ output += '"';
850
+ index += 1;
851
+ continue;
852
+ }
853
+ if (next === "'") {
854
+ output += "'";
855
+ index += 1;
856
+ continue;
857
+ }
858
+ if (next === "\\") {
859
+ output += "\\";
860
+ index += 1;
861
+ continue;
862
+ }
863
+ }
864
+ output += char;
865
+ }
866
+ return output;
867
+ };
868
+ var normalizeJsLikeJson = (input) => {
869
+ const normalizedQuotes = normalizeSmartQuotes(input);
870
+ const normalizedSingleQuotes = convertSingleQuotedStrings(normalizedQuotes);
871
+ const withQuotedKeys = quoteBareObjectKeys(normalizedSingleQuotes);
872
+ return stripTrailingCommas(withQuotedKeys);
873
+ };
874
+ var extractFirstJsonObject = (input) => {
875
+ const start = input.indexOf("{");
876
+ if (start === -1) return null;
877
+ let depth = 0;
878
+ let inString = false;
879
+ let quoteChar = "";
880
+ let escaped = false;
881
+ for (let index = start; index < input.length; index += 1) {
882
+ const char = input[index];
883
+ if (inString) {
884
+ if (escaped) {
885
+ escaped = false;
886
+ continue;
887
+ }
888
+ if (char === "\\") {
889
+ escaped = true;
890
+ continue;
891
+ }
892
+ if (char === quoteChar) {
893
+ inString = false;
894
+ quoteChar = "";
895
+ }
896
+ continue;
897
+ }
898
+ if (char === '"' || char === "'") {
899
+ inString = true;
900
+ quoteChar = char;
901
+ continue;
902
+ }
903
+ if (char === "{") {
904
+ depth += 1;
905
+ continue;
906
+ }
907
+ if (char === "}") {
908
+ depth -= 1;
909
+ if (depth === 0) {
910
+ return input.slice(start, index + 1);
911
+ }
912
+ }
913
+ }
914
+ return null;
915
+ };
916
+ var parseMaybeNestedJson = (input) => {
917
+ let current = input;
918
+ for (let depth = 0; depth < TOOL_ARGS_CANDIDATE_PARSE_DEPTH; depth += 1) {
919
+ if (typeof current !== "string") {
920
+ return current;
921
+ }
922
+ const trimmed = current.trim();
923
+ if (!trimmed) {
924
+ return {};
925
+ }
926
+ current = JSON.parse(trimmed);
927
+ }
928
+ return current;
929
+ };
930
+ var addCandidate = (candidates, value) => {
931
+ if (typeof value !== "string") return;
932
+ const trimmed = value.trim();
933
+ if (!trimmed) return;
934
+ candidates.add(trimmed);
935
+ };
936
+ var buildProgressiveQuoteUnescapes = (input, rounds = 4) => {
937
+ const values = [];
938
+ let current = input;
939
+ for (let round = 0; round < rounds; round += 1) {
940
+ const next = current.replace(/\\"/gu, '"');
941
+ if (next === current) {
942
+ break;
943
+ }
944
+ values.push(next);
945
+ current = next;
946
+ }
947
+ return values;
948
+ };
949
+ var parseToolArguments = (rawArgs) => {
950
+ if (isPlainObject(rawArgs)) {
951
+ return rawArgs;
952
+ }
953
+ if (typeof rawArgs !== "string") {
954
+ return {};
955
+ }
956
+ const base = rawArgs.trim();
957
+ if (!base) {
958
+ return {};
959
+ }
960
+ const codeFenceStripped = stripMarkdownCodeFence(base);
961
+ const extracted = extractFirstJsonObject(codeFenceStripped);
962
+ const candidates = /* @__PURE__ */ new Set();
963
+ addCandidate(candidates, base);
964
+ addCandidate(candidates, codeFenceStripped);
965
+ addCandidate(candidates, stripOuterQuotes(codeFenceStripped));
966
+ buildProgressiveQuoteUnescapes(base).forEach((candidate) => addCandidate(candidates, candidate));
967
+ buildProgressiveQuoteUnescapes(codeFenceStripped).forEach(
968
+ (candidate) => addCandidate(candidates, candidate)
969
+ );
970
+ addCandidate(candidates, extracted);
971
+ buildProgressiveQuoteUnescapes(extracted || "").forEach(
972
+ (candidate) => addCandidate(candidates, candidate)
973
+ );
974
+ addCandidate(candidates, normalizeJsLikeJson(codeFenceStripped));
975
+ addCandidate(candidates, extracted ? normalizeJsLikeJson(extracted) : null);
976
+ Array.from(candidates).forEach((candidate) => {
977
+ addCandidate(candidates, normalizeEscapesOutsideStrings(candidate));
978
+ });
979
+ for (const candidate of candidates) {
980
+ try {
981
+ const parsed = parseMaybeNestedJson(candidate);
982
+ if (isPlainObject(parsed)) {
983
+ return parsed;
984
+ }
985
+ } catch (e) {
986
+ continue;
987
+ }
988
+ }
989
+ return null;
990
+ };
991
+
696
992
  // src/ChatPanel.tsx
697
993
  var ChatPanel = ({
698
994
  project_id,
@@ -738,7 +1034,8 @@ var ChatPanel = ({
738
1034
  customerEmailCaptureMode = "HIDE",
739
1035
  customerEmailCapturePlaceholder = "Please enter your email...",
740
1036
  mcpServers,
741
- progressiveActions = true
1037
+ progressiveActions = true,
1038
+ resolveMcpAuthHeaders
742
1039
  }) => {
743
1040
  var _a;
744
1041
  const isEmailAddress = (email) => {
@@ -1064,6 +1361,44 @@ var ChatPanel = ({
1064
1361
  const [toolList, setToolList] = (0, import_react11.useState)([]);
1065
1362
  const [toolsLoading, setToolsLoading] = (0, import_react11.useState)(false);
1066
1363
  const [toolsFetchError, setToolsFetchError] = (0, import_react11.useState)(false);
1364
+ const buildMcpRequestHeaders = (0, import_react11.useCallback)(
1365
+ (_0) => __async(void 0, [_0], function* ({
1366
+ phase,
1367
+ mcpServer,
1368
+ toolName,
1369
+ toolArgs
1370
+ }) {
1371
+ const merged = {};
1372
+ const baseAccessToken = typeof mcpServer.accessToken === "string" ? mcpServer.accessToken.trim() : "";
1373
+ if (baseAccessToken) {
1374
+ merged["x-mcp-access-token"] = baseAccessToken;
1375
+ }
1376
+ if (project_id) {
1377
+ merged["x-project-id"] = project_id;
1378
+ }
1379
+ if (!resolveMcpAuthHeaders) return merged;
1380
+ try {
1381
+ const resolved = yield resolveMcpAuthHeaders({
1382
+ phase,
1383
+ mcpServer,
1384
+ projectId: project_id,
1385
+ customer: currentCustomer,
1386
+ toolName,
1387
+ toolArgs
1388
+ });
1389
+ return __spreadValues(__spreadValues({}, merged), normalizeMcpHeaders(
1390
+ resolved
1391
+ ));
1392
+ } catch (error2) {
1393
+ console.error(
1394
+ `Failed to resolve MCP auth headers for ${phase} request:`,
1395
+ error2
1396
+ );
1397
+ return merged;
1398
+ }
1399
+ }),
1400
+ [project_id, currentCustomer, resolveMcpAuthHeaders]
1401
+ );
1067
1402
  (0, import_react11.useEffect)(() => {
1068
1403
  const fetchAndSetTools = () => __async(void 0, null, function* () {
1069
1404
  if (!mcpServers || mcpServers.length === 0) {
@@ -1080,7 +1415,13 @@ var ChatPanel = ({
1080
1415
  m.url
1081
1416
  )}`;
1082
1417
  try {
1083
- const response2 = yield fetch(urlToFetch);
1418
+ const requestHeaders = yield buildMcpRequestHeaders({
1419
+ phase: "list",
1420
+ mcpServer: m
1421
+ });
1422
+ const response2 = yield fetch(urlToFetch, {
1423
+ headers: requestHeaders
1424
+ });
1084
1425
  if (!response2.ok) {
1085
1426
  console.error(
1086
1427
  `Error fetching tools from ${m.url}: ${response2.status} ${response2.statusText}`
@@ -1096,7 +1437,7 @@ var ChatPanel = ({
1096
1437
  return toolsFromServer.map((tool) => __spreadProps(__spreadValues({}, tool), {
1097
1438
  url: m.url,
1098
1439
  accessToken: m.accessToken || "",
1099
- headers: {}
1440
+ headers: requestHeaders
1100
1441
  }));
1101
1442
  } else {
1102
1443
  return [];
@@ -1125,7 +1466,7 @@ var ChatPanel = ({
1125
1466
  }
1126
1467
  });
1127
1468
  fetchAndSetTools();
1128
- }, [mcpServers, publicAPIUrl]);
1469
+ }, [mcpServers, publicAPIUrl, buildMcpRequestHeaders]);
1129
1470
  const llmResult = (0, import_llmasaservice_client.useLLM)({
1130
1471
  project_id,
1131
1472
  customer: currentCustomer,
@@ -1490,19 +1831,19 @@ var ChatPanel = ({
1490
1831
  []
1491
1832
  );
1492
1833
  const anthropic_toolAction = {
1493
- pattern: '\\{"type":"tool_use","id":"([^"]+)","name":"([^"]+)","input":(\\{[\\s\\S]+?\\}),"service":"([^"]+)"\\}',
1834
+ pattern: '\\{"type":"tool_use","id":"([^"]+)","name":"([^"]+)","input":(\\{[\\s\\S]*?\\}),"service":"([^"]+)"\\}',
1494
1835
  type: "markdown",
1495
1836
  markdown: "<br />*Tool use requested: $2*",
1496
1837
  actionType: "tool"
1497
1838
  };
1498
1839
  const openAI_toolAction = {
1499
- pattern: '\\{"id":"([^"]+)","type":"function","function":\\{"name":"([^"]+)","arguments":"((?:\\\\.|[^"\\\\])*)"\\},"service":"([^"]+)"\\}',
1840
+ pattern: '\\{"id":"([^"]+)","type":"function","function":\\{"name":"([^"]+)","arguments":"([\\s\\S]*?)"\\},"service":"([^"]+)"\\}',
1500
1841
  type: "markdown",
1501
1842
  markdown: "<br />*Tool use requested: $2*",
1502
1843
  actionType: "tool"
1503
1844
  };
1504
1845
  const google_toolAction = {
1505
- pattern: '^\\{\\s*"(functionCall)"\\s*:\\s*\\{\\s*"name"\\s*:\\s*"([^"]+)"\\s*,\\s*"args"\\s*:\\s*(\\{[\\s\\S]+?\\})\\s*\\}(?:\\s*,\\s*"thoughtSignature"\\s*:\\s*"[^"]*")?\\s*,\\s*"service"\\s*:\\s*"([^"]+)"\\s*\\}$',
1846
+ pattern: '^\\{\\s*"(functionCall)"\\s*:\\s*\\{\\s*"name"\\s*:\\s*"([^"]+)"\\s*,\\s*"args"\\s*:\\s*(\\{[\\s\\S]*?\\})\\s*\\}(?:\\s*,\\s*"thoughtSignature"\\s*:\\s*"[^"]*")?\\s*,\\s*"service"\\s*:\\s*"([^"]+)"\\s*\\}$',
1506
1847
  type: "markdown",
1507
1848
  markdown: "<br />*Tool use requested: $2*",
1508
1849
  actionType: "tool"
@@ -1644,114 +1985,125 @@ var ChatPanel = ({
1644
1985
  ]
1645
1986
  }
1646
1987
  ];
1647
- const toolCallsMessage = {
1648
- role: "assistant",
1649
- content: [],
1650
- tool_calls: []
1651
- };
1652
- const toolCallsPromises = toolsToProcess.map((req) => __async(void 0, null, function* () {
1653
- if (!req) return null;
1654
- try {
1988
+ const parsedToolCalls = yield Promise.all(
1989
+ toolsToProcess.map((req, index) => __async(void 0, null, function* () {
1990
+ var _a2, _b, _c, _d, _e, _f;
1991
+ if (!req) return null;
1992
+ let parsedToolCall = null;
1993
+ try {
1994
+ parsedToolCall = JSON.parse(req.match);
1995
+ } catch (error2) {
1996
+ console.error("Failed to parse tool call:", error2);
1997
+ }
1998
+ const toolName = req.groups[1] || req.toolName || (typeof (parsedToolCall == null ? void 0 : parsedToolCall.name) === "string" ? parsedToolCall.name : "") || (typeof ((_a2 = parsedToolCall == null ? void 0 : parsedToolCall.function) == null ? void 0 : _a2.name) === "string" ? parsedToolCall.function.name : "");
1999
+ if (!toolName) return null;
2000
+ const rawCallId = req.groups[0] || (parsedToolCall == null ? void 0 : parsedToolCall.id) || (parsedToolCall == null ? void 0 : parsedToolCall.tool_call_id) || `${toolName}-${index + 1}`;
2001
+ const callId = typeof rawCallId === "string" && rawCallId.trim().length > 0 && rawCallId !== "functionCall" ? rawCallId : `${toolName}-${index + 1}`;
2002
+ let args = {};
2003
+ const rawArgs = (_f = (_e = (_c = (_b = req.groups[2]) != null ? _b : parsedToolCall == null ? void 0 : parsedToolCall.input) != null ? _c : parsedToolCall == null ? void 0 : parsedToolCall.args) != null ? _e : (_d = parsedToolCall == null ? void 0 : parsedToolCall.function) == null ? void 0 : _d.arguments) != null ? _f : "{}";
2004
+ const parsedArgs = parseToolArguments(rawArgs);
2005
+ if (!parsedArgs) {
2006
+ console.error("Failed to parse tool arguments", {
2007
+ toolName,
2008
+ callId,
2009
+ rawArgsPreview: typeof rawArgs === "string" ? rawArgs.slice(0, 500) : JSON.stringify(rawArgs).slice(0, 500)
2010
+ });
2011
+ return null;
2012
+ }
2013
+ args = parsedArgs;
2014
+ const serviceTag = typeof req.groups[3] === "string" && req.groups[3] || typeof (parsedToolCall == null ? void 0 : parsedToolCall.service) === "string" && parsedToolCall.service || "";
1655
2015
  return {
1656
2016
  req,
1657
- parsedToolCall: JSON.parse(req.match)
2017
+ toolName,
2018
+ callId,
2019
+ args,
2020
+ serviceTag
1658
2021
  };
1659
- } catch (e) {
1660
- console.error("Failed to parse tool call:", e);
1661
- return null;
1662
- }
2022
+ }))
2023
+ );
2024
+ const toolCallBatch = parsedToolCalls.filter(Boolean);
2025
+ const finalToolCalls = toolCallBatch.map((toolCall) => ({
2026
+ id: toolCall.callId,
2027
+ type: "tool_use",
2028
+ name: toolCall.toolName,
2029
+ input: toolCall.args,
2030
+ service: toolCall.serviceTag
1663
2031
  }));
1664
- const parsedToolCalls = yield Promise.all(toolCallsPromises);
1665
- parsedToolCalls.forEach((item) => {
1666
- if (item && item.parsedToolCall) {
1667
- toolCallsMessage.tool_calls.push(item.parsedToolCall);
1668
- }
1669
- });
1670
- newMessages.push(toolCallsMessage);
1671
- const finalToolCalls = toolCallsMessage.tool_calls;
1672
- const toolResponsePromises = parsedToolCalls.map((item) => __async(void 0, null, function* () {
1673
- var _a2;
1674
- if (!item || !item.req) return null;
1675
- const req = item.req;
1676
- const mcpTool = toolList.find((tool) => tool.name === req.toolName);
2032
+ const toolResponsePromises = toolCallBatch.map((toolCall) => __async(void 0, null, function* () {
2033
+ var _a2, _b, _c;
2034
+ const mcpTool = toolList.find((tool) => tool.name === toolCall.toolName);
1677
2035
  if (!mcpTool) {
1678
- console.error(`Tool ${req.toolName} not found in tool list`);
1679
- return null;
2036
+ console.error(`Tool ${toolCall.toolName} not found in tool list`);
2037
+ return {
2038
+ tool_call_id: toolCall.callId,
2039
+ tool_name: toolCall.toolName,
2040
+ result: `Tool ${toolCall.toolName} not found in current tool list.`,
2041
+ isError: true
2042
+ };
1680
2043
  }
1681
2044
  try {
1682
- let args;
1683
- try {
1684
- args = JSON.parse(req.groups[2]);
1685
- } catch (e) {
1686
- try {
1687
- args = JSON.parse(req.groups[2].replace(/\\"/g, '"'));
1688
- } catch (err) {
1689
- console.error("Failed to parse tool arguments:", err);
1690
- return null;
1691
- }
1692
- }
1693
2045
  const body = {
1694
- tool: req.groups[1],
1695
- args
2046
+ tool: toolCall.toolName,
2047
+ args: toolCall.args
1696
2048
  };
1697
2049
  const result = yield fetch(
1698
2050
  `${publicAPIUrl}/tools/${encodeURIComponent(mcpTool.url)}`,
1699
2051
  {
1700
2052
  method: "POST",
1701
- headers: {
1702
- "Content-Type": "application/json",
1703
- "x-mcp-access-token": mcpTool.accessToken && mcpTool.accessToken !== "" ? mcpTool.accessToken : "",
1704
- "x-project-id": project_id
1705
- },
2053
+ headers: __spreadValues({
2054
+ "Content-Type": "application/json"
2055
+ }, yield buildMcpRequestHeaders({
2056
+ phase: "call",
2057
+ mcpServer: mcpTool,
2058
+ toolName: toolCall.toolName,
2059
+ toolArgs: toolCall.args
2060
+ })),
1706
2061
  body: JSON.stringify(body)
1707
2062
  }
1708
2063
  );
1709
2064
  if (!result.ok) {
1710
2065
  console.error(
1711
- `Error calling tool ${req.toolName}: ${result.status} ${result.statusText}`
2066
+ `Error calling tool ${toolCall.toolName}: ${result.status} ${result.statusText}`
1712
2067
  );
1713
2068
  const errorBody = yield result.text();
1714
2069
  console.error(`Error body: ${errorBody}`);
1715
- return null;
2070
+ return {
2071
+ tool_call_id: toolCall.callId,
2072
+ tool_name: toolCall.toolName,
2073
+ result: `HTTP ${result.status} ${result.statusText}: ${errorBody || "Tool call failed"}`,
2074
+ isError: true
2075
+ };
1716
2076
  }
1717
2077
  let resultData;
1718
2078
  try {
1719
2079
  resultData = yield result.json();
1720
2080
  } catch (jsonError) {
1721
2081
  console.error(
1722
- `Error parsing JSON response for tool ${req.toolName}:`,
2082
+ `Error parsing JSON response for tool ${toolCall.toolName}:`,
1723
2083
  jsonError
1724
2084
  );
1725
- try {
1726
- const textBody = yield result.text();
1727
- console.error("Response body (text):", textBody);
1728
- } catch (textError) {
1729
- console.error(
1730
- "Failed to read response body as text either:",
1731
- textError
1732
- );
1733
- }
1734
- return null;
1735
- }
1736
- if (resultData && resultData.content && resultData.content.length > 0) {
1737
- const textResult = (_a2 = resultData.content[0]) == null ? void 0 : _a2.text;
1738
2085
  return {
1739
- role: "tool",
1740
- content: [
1741
- {
1742
- type: "text",
1743
- text: textResult
1744
- }
1745
- ],
1746
- tool_call_id: req.groups[0]
2086
+ tool_call_id: toolCall.callId,
2087
+ tool_name: toolCall.toolName,
2088
+ result: "Tool returned a non-JSON response.",
2089
+ isError: true
1747
2090
  };
1748
- } else {
1749
- console.error(`No content returned from tool ${req.toolName}`);
1750
- return null;
1751
2091
  }
2092
+ const textResult = (_c = (_b = (_a2 = resultData == null ? void 0 : resultData.content) == null ? void 0 : _a2[0]) == null ? void 0 : _b.text) != null ? _c : (resultData == null ? void 0 : resultData.result) ? JSON.stringify(resultData.result) : JSON.stringify(resultData);
2093
+ return {
2094
+ tool_call_id: toolCall.callId,
2095
+ tool_name: toolCall.toolName,
2096
+ result: textResult || "",
2097
+ isError: (resultData == null ? void 0 : resultData.isError) === true
2098
+ };
1752
2099
  } catch (error2) {
1753
- console.error(`Error processing tool ${req.toolName}:`, error2);
1754
- return null;
2100
+ console.error(`Error processing tool ${toolCall.toolName}:`, error2);
2101
+ return {
2102
+ tool_call_id: toolCall.callId,
2103
+ tool_name: toolCall.toolName,
2104
+ result: error2 instanceof Error ? error2.message : `Unhandled error calling ${toolCall.toolName}`,
2105
+ isError: true
2106
+ };
1755
2107
  }
1756
2108
  }));
1757
2109
  const toolResponses = yield Promise.all(toolResponsePromises);
@@ -1773,11 +2125,49 @@ var ChatPanel = ({
1773
2125
  });
1774
2126
  });
1775
2127
  }
1776
- finalToolResponses.forEach((response2) => {
1777
- if (response2) {
1778
- newMessages.push(response2);
1779
- }
1780
- });
2128
+ const toReplayText = (value, maxLength = 2e3) => {
2129
+ const raw = typeof value === "string" ? value : (() => {
2130
+ try {
2131
+ return JSON.stringify(value);
2132
+ } catch (_error) {
2133
+ return String(value != null ? value : "");
2134
+ }
2135
+ })();
2136
+ const normalized = String(raw != null ? raw : "").replace(/\s+/g, " ").trim();
2137
+ if (normalized.length <= maxLength) return normalized;
2138
+ return `${normalized.slice(0, maxLength - 3)}...`;
2139
+ };
2140
+ if (toolCallBatch.length > 0) {
2141
+ const replayLines = toolCallBatch.map((toolCall, index) => {
2142
+ var _a2;
2143
+ const matchedResponse = finalToolResponses.find(
2144
+ (response2) => (response2 == null ? void 0 : response2.tool_call_id) === toolCall.callId
2145
+ ) || finalToolResponses[index];
2146
+ const status = (matchedResponse == null ? void 0 : matchedResponse.isError) ? "error" : "ok";
2147
+ const resultText = toReplayText((_a2 = matchedResponse == null ? void 0 : matchedResponse.result) != null ? _a2 : "No result returned");
2148
+ return [
2149
+ `Tool: ${toolCall.toolName}`,
2150
+ `Call ID: ${toolCall.callId}`,
2151
+ `Status: ${status}`,
2152
+ `Args: ${toReplayText(toolCall.args, 600)}`,
2153
+ `Result: ${resultText}`
2154
+ ].join("\n");
2155
+ });
2156
+ newMessages.push({
2157
+ role: "user",
2158
+ content: [
2159
+ {
2160
+ type: "text",
2161
+ text: [
2162
+ "Tool execution summary for the previous request:",
2163
+ ...replayLines,
2164
+ "Continue the same assistant response from exactly where you paused using these tool results.",
2165
+ "If this response is using meta tags, keep the same format (<thinking>, <reasoning>, <searching>) in the continuation."
2166
+ ].join("\n\n")
2167
+ }
2168
+ ]
2169
+ });
2170
+ }
1781
2171
  send(
1782
2172
  "",
1783
2173
  newMessages,
@@ -3559,7 +3949,8 @@ var AgentPanel = ({
3559
3949
  //ragQueryLimit = 10,
3560
3950
  //ragRankLimit = 5,
3561
3951
  initialHistory = {},
3562
- hideRagContextInPrompt = true
3952
+ hideRagContextInPrompt = true,
3953
+ resolveMcpAuthHeaders
3563
3954
  }) => {
3564
3955
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u;
3565
3956
  const searchParams = new URLSearchParams(location.search);
@@ -3697,7 +4088,8 @@ var AgentPanel = ({
3697
4088
  createConversationOnFirstChat: (_s = agentData == null ? void 0 : agentData.createConversationOnFirstChat) != null ? _s : true,
3698
4089
  customerEmailCaptureMode: (_t = agentData == null ? void 0 : agentData.customerEmailCaptureMode) != null ? _t : "HIDE",
3699
4090
  customerEmailCapturePlaceholder: (_u = agentData == null ? void 0 : agentData.customerEmailCapturePlaceholder) != null ? _u : "Please enter your email...",
3700
- mcpServers: mcpData
4091
+ mcpServers: mcpData,
4092
+ resolveMcpAuthHeaders
3701
4093
  })
3702
4094
  ));
3703
4095
  };
@@ -3768,18 +4160,731 @@ var ToolInfoModal2 = ({
3768
4160
  var ToolInfoModal_default2 = ToolInfoModal2;
3769
4161
 
3770
4162
  // src/AIChatPanel.tsx
4163
+ var areToolRequestListsEqual = (a, b) => {
4164
+ if (a.length !== b.length) return false;
4165
+ for (let index = 0; index < a.length; index += 1) {
4166
+ const left = a[index];
4167
+ const right = b[index];
4168
+ if (!left || !right) return false;
4169
+ if (left.callId !== right.callId) return false;
4170
+ if (left.toolName !== right.toolName) return false;
4171
+ if (left.serviceTag !== right.serviceTag) return false;
4172
+ if (left.match !== right.match) return false;
4173
+ if (left.start !== right.start || left.end !== right.end) return false;
4174
+ }
4175
+ return true;
4176
+ };
4177
+ var mergeContinuationResponseText = (baseText, continuationText) => {
4178
+ const base = typeof baseText === "string" ? baseText : "";
4179
+ const continuation = typeof continuationText === "string" ? continuationText : "";
4180
+ if (!base) return continuation;
4181
+ if (!continuation) return base;
4182
+ if (base.includes(continuation)) {
4183
+ return base;
4184
+ }
4185
+ const maxOverlap = Math.min(base.length, continuation.length);
4186
+ for (let overlap = maxOverlap; overlap > 0; overlap -= 1) {
4187
+ if (base.slice(-overlap) === continuation.slice(0, overlap)) {
4188
+ return `${base}${continuation.slice(overlap)}`;
4189
+ }
4190
+ }
4191
+ return `${base}
4192
+
4193
+ ${continuation}`;
4194
+ };
4195
+ var INLINE_TOOL_MARKER_PREFIX = "[[AI_TOOL_CALL:";
4196
+ var INLINE_TOOL_MARKER_SUFFIX = "]]";
4197
+ var INLINE_TOOL_MARKER_REGEX = /\[\[AI_TOOL_CALL:([^|\]]+)\|([^\]]+)\]\]/g;
4198
+ var INLINE_THINKING_MARKER_PREFIX = "[[AI_THINK_BLOCK:";
4199
+ var INLINE_THINKING_MARKER_SUFFIX = "]]";
4200
+ var INLINE_THINKING_MARKER_REGEX = /\[\[AI_THINK_BLOCK:([^|\]]+)\|([^\]]+)\]\]/g;
4201
+ var MAX_TOOL_CONTINUATIONS_PER_TURN = 20;
4202
+ var MAX_TOOL_REPLAY_PAYLOAD_CHARS = 75e4;
4203
+ var MAX_TRACE_SUMMARY_CHARS = 1800;
4204
+ var MAX_TRACE_REASONING_BLOCKS = 4;
4205
+ var MAX_TRACE_TOOL_LINES = 6;
4206
+ var MAX_TRACE_ITEM_CHARS = 220;
4207
+ var MAX_TRACE_LINE_CHARS = 420;
4208
+ var hasInlineRuntimeMarkers = (value) => {
4209
+ const source = typeof value === "string" ? value : "";
4210
+ return source.includes(INLINE_TOOL_MARKER_PREFIX) || source.includes(INLINE_THINKING_MARKER_PREFIX);
4211
+ };
4212
+ var toNonEmptyLines = (value) => String(value || "").split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
4213
+ var isBoundarySubsetByLines = (existingLines, incomingLines) => {
4214
+ if (incomingLines.length === 0 || existingLines.length === 0) return false;
4215
+ if (incomingLines.length >= existingLines.length) return false;
4216
+ const prefixMatch = incomingLines.every((line, index) => existingLines[index] === line);
4217
+ if (prefixMatch) return true;
4218
+ const suffixStart = existingLines.length - incomingLines.length;
4219
+ return incomingLines.every((line, index) => existingLines[suffixStart + index] === line);
4220
+ };
4221
+ var shouldPreserveBoundaryDroppedStreamText = (existingContent, incomingContent) => {
4222
+ const existing = String(existingContent || "").trim();
4223
+ const incoming = String(incomingContent || "").trim();
4224
+ if (!existing) return false;
4225
+ if (!incoming) return true;
4226
+ if (incoming === existing) return false;
4227
+ if (!hasInlineRuntimeMarkers(existing)) return false;
4228
+ if (existing.includes(incoming)) {
4229
+ return true;
4230
+ }
4231
+ const existingLines = toNonEmptyLines(existing);
4232
+ const incomingLines = toNonEmptyLines(incoming);
4233
+ return isBoundarySubsetByLines(existingLines, incomingLines);
4234
+ };
4235
+ var isObjectRecord = (value) => !!value && typeof value === "object" && !Array.isArray(value);
4236
+ var stringifyToolArgs = (value) => {
4237
+ if (typeof value === "string") return value;
4238
+ try {
4239
+ return JSON.stringify(value != null ? value : {});
4240
+ } catch (_error) {
4241
+ return "{}";
4242
+ }
4243
+ };
4244
+ var truncateTraceText = (value, maxChars) => {
4245
+ const text = String(value || "").trim();
4246
+ if (!text) return "";
4247
+ if (text.length <= maxChars) return text;
4248
+ return `${text.slice(0, Math.max(0, maxChars - 1)).trimEnd()}\u2026`;
4249
+ };
4250
+ var normalizeTraceText = (value) => {
4251
+ if (value === null || value === void 0) return "";
4252
+ const raw = typeof value === "string" ? value : (() => {
4253
+ try {
4254
+ return JSON.stringify(value);
4255
+ } catch (_error) {
4256
+ return String(value);
4257
+ }
4258
+ })();
4259
+ return String(raw || "").replace(/\s+/g, " ").trim();
4260
+ };
4261
+ var toTraceObjectArray = (value) => {
4262
+ if (!Array.isArray(value)) return [];
4263
+ return value.filter((item) => isObjectRecord(item));
4264
+ };
4265
+ var getTraceStatusLabel = (response) => {
4266
+ if (!response) return "pending";
4267
+ if (response.isError === true || response.error === true) return "error";
4268
+ return "ok";
4269
+ };
4270
+ var buildCompactTraceSummary = ({
4271
+ reasoningBlocks,
4272
+ toolCalls,
4273
+ toolResponses
4274
+ }) => {
4275
+ const sections = [];
4276
+ const normalizedReasoning = (Array.isArray(reasoningBlocks) ? reasoningBlocks : []).filter((block) => !!block && typeof block.content === "string" && block.content.trim().length > 0).slice(-MAX_TRACE_REASONING_BLOCKS).map((block) => {
4277
+ const content = truncateTraceText(normalizeTraceText(block.content), MAX_TRACE_ITEM_CHARS);
4278
+ if (!content) return "";
4279
+ const type = typeof block.type === "string" ? block.type : "thinking";
4280
+ return `- ${type}: ${content}`;
4281
+ }).filter(Boolean);
4282
+ if (normalizedReasoning.length > 0) {
4283
+ sections.push(["reasoning:", ...normalizedReasoning].join("\n"));
4284
+ }
4285
+ const calls = toTraceObjectArray(toolCalls).slice(-MAX_TRACE_TOOL_LINES);
4286
+ const responses = toTraceObjectArray(toolResponses);
4287
+ if (calls.length > 0) {
4288
+ const responsesByCallId = /* @__PURE__ */ new Map();
4289
+ responses.forEach((response) => {
4290
+ const key = typeof response.tool_call_id === "string" ? response.tool_call_id.trim() : "";
4291
+ if (!key || responsesByCallId.has(key)) return;
4292
+ responsesByCallId.set(key, response);
4293
+ });
4294
+ const responseOffset = Math.max(0, responses.length - calls.length);
4295
+ const toolLines = calls.map((call, index) => {
4296
+ var _a, _b, _c, _d, _e, _f;
4297
+ const toolName = typeof call.name === "string" ? call.name.trim() : typeof call.tool_name === "string" ? call.tool_name.trim() : "tool";
4298
+ const callId = typeof call.id === "string" ? call.id.trim() : typeof call.tool_call_id === "string" ? call.tool_call_id.trim() : "";
4299
+ const response = (callId ? responsesByCallId.get(callId) : void 0) || responses[responseOffset + index] || responses[index];
4300
+ const status = getTraceStatusLabel(response);
4301
+ const rawArgs = (_c = (_b = (_a = call.input) != null ? _a : call.args) != null ? _b : call.arguments) != null ? _c : {};
4302
+ const parsedArgs = parseToolArguments(rawArgs);
4303
+ const normalizedArgs = truncateTraceText(
4304
+ normalizeTraceText(parsedArgs != null ? parsedArgs : isObjectRecord(rawArgs) ? rawArgs : rawArgs || {}),
4305
+ MAX_TRACE_ITEM_CHARS
4306
+ );
4307
+ const normalizedResult = truncateTraceText(
4308
+ normalizeTraceText((_f = (_e = (_d = response == null ? void 0 : response.result) != null ? _d : response == null ? void 0 : response.content) != null ? _e : response == null ? void 0 : response.error) != null ? _f : ""),
4309
+ MAX_TRACE_ITEM_CHARS
4310
+ );
4311
+ const base = callId ? `- ${toolName} (${callId}) ${status}` : `- ${toolName} ${status}`;
4312
+ const withArgs = normalizedArgs ? `${base} args=${normalizedArgs}` : base;
4313
+ const withResult = normalizedResult ? `${withArgs} result=${normalizedResult}` : withArgs;
4314
+ return truncateTraceText(withResult, MAX_TRACE_LINE_CHARS);
4315
+ }).filter(Boolean);
4316
+ if (toolLines.length > 0) {
4317
+ sections.push(["tools:", ...toolLines].join("\n"));
4318
+ }
4319
+ }
4320
+ if (sections.length === 0) return "";
4321
+ return truncateTraceText(["TRACE SUMMARY (compact)", ...sections].join("\n"), MAX_TRACE_SUMMARY_CHARS);
4322
+ };
4323
+ var findPreviousNonWhitespaceChar = (text, startIndex) => {
4324
+ for (let index = startIndex; index >= 0; index -= 1) {
4325
+ const char = text[index];
4326
+ if (typeof char !== "string") continue;
4327
+ if (!/\s/.test(char)) {
4328
+ return char;
4329
+ }
4330
+ }
4331
+ return null;
4332
+ };
4333
+ var findNextNonWhitespaceChar = (text, startIndex) => {
4334
+ for (let index = startIndex; index < text.length; index += 1) {
4335
+ const char = text[index];
4336
+ if (typeof char !== "string") continue;
4337
+ if (!/\s/.test(char)) {
4338
+ return char;
4339
+ }
4340
+ }
4341
+ return null;
4342
+ };
4343
+ var isStandaloneToolObjectSegment = (text, openIndex, closeIndex) => {
4344
+ const lineStart = text.lastIndexOf("\n", openIndex - 1) + 1;
4345
+ const lineEndRaw = text.indexOf("\n", closeIndex + 1);
4346
+ const lineEnd = lineEndRaw === -1 ? text.length : lineEndRaw;
4347
+ const linePrefix = text.slice(lineStart, openIndex);
4348
+ const lineSuffix = text.slice(closeIndex + 1, lineEnd);
4349
+ const startsLine = linePrefix.trim().length === 0;
4350
+ const endsLine = lineSuffix.trim().length === 0;
4351
+ const prevChar = findPreviousNonWhitespaceChar(text, openIndex - 1);
4352
+ const nextChar = findNextNonWhitespaceChar(text, closeIndex + 1);
4353
+ const prevDelimited = prevChar !== null && ["{", "}", "[", "]", ","].includes(prevChar);
4354
+ const nextDelimited = nextChar !== null && ["{", "}", "[", "]", ","].includes(nextChar);
4355
+ const prefixWindow = text.slice(Math.max(0, openIndex - 64), openIndex);
4356
+ const hasLabelPrefix = /(?:^|[\r\n])\s*[A-Za-z][A-Za-z0-9 _-]{0,30}:\s*$/.test(prefixWindow);
4357
+ if (hasLabelPrefix) return false;
4358
+ return (startsLine || prevDelimited) && (endsLine || nextDelimited);
4359
+ };
4360
+ var extractTopLevelJsonObjectSegments = (source) => {
4361
+ const text = typeof source === "string" ? source : "";
4362
+ if (!text) return [];
4363
+ const segments = [];
4364
+ let cursor = 0;
4365
+ while (cursor < text.length) {
4366
+ const openIndex = text.indexOf("{", cursor);
4367
+ if (openIndex === -1) break;
4368
+ let depth = 0;
4369
+ let inString = false;
4370
+ let escaped = false;
4371
+ let closeIndex = -1;
4372
+ for (let index = openIndex; index < text.length; index += 1) {
4373
+ const char = text[index];
4374
+ if (inString) {
4375
+ if (escaped) {
4376
+ escaped = false;
4377
+ continue;
4378
+ }
4379
+ if (char === "\\") {
4380
+ escaped = true;
4381
+ continue;
4382
+ }
4383
+ if (char === '"') {
4384
+ inString = false;
4385
+ }
4386
+ continue;
4387
+ }
4388
+ if (char === '"') {
4389
+ inString = true;
4390
+ continue;
4391
+ }
4392
+ if (char === "{") {
4393
+ depth += 1;
4394
+ continue;
4395
+ }
4396
+ if (char === "}") {
4397
+ depth -= 1;
4398
+ if (depth === 0) {
4399
+ closeIndex = index;
4400
+ break;
4401
+ }
4402
+ if (depth < 0) {
4403
+ break;
4404
+ }
4405
+ }
4406
+ }
4407
+ if (closeIndex === -1) {
4408
+ cursor = openIndex + 1;
4409
+ continue;
4410
+ }
4411
+ const raw = text.slice(openIndex, closeIndex + 1);
4412
+ try {
4413
+ const value = JSON.parse(raw);
4414
+ if (isObjectRecord(value) && isStandaloneToolObjectSegment(text, openIndex, closeIndex)) {
4415
+ segments.push({
4416
+ start: openIndex,
4417
+ end: closeIndex + 1,
4418
+ raw,
4419
+ value
4420
+ });
4421
+ }
4422
+ } catch (_error) {
4423
+ }
4424
+ cursor = closeIndex + 1;
4425
+ }
4426
+ return segments;
4427
+ };
4428
+ var resolveToolRequestFromSegment = (segment, fallbackIndex) => {
4429
+ var _a, _b, _c;
4430
+ const record = segment.value;
4431
+ const serviceTag = typeof record.service === "string" ? record.service : "";
4432
+ const rawId = typeof record.id === "string" ? record.id.trim() : "";
4433
+ if (record.type === "tool_use" && typeof record.name === "string") {
4434
+ const toolName = String(record.name).trim();
4435
+ if (!toolName) return null;
4436
+ const callId = rawId || `${toolName}-${fallbackIndex + 1}`;
4437
+ return {
4438
+ match: segment.raw,
4439
+ groups: [callId, toolName, stringifyToolArgs((_a = record.input) != null ? _a : {}), serviceTag],
4440
+ toolName,
4441
+ callId,
4442
+ serviceTag,
4443
+ start: segment.start,
4444
+ end: segment.end
4445
+ };
4446
+ }
4447
+ if (record.type === "function" && isObjectRecord(record.function)) {
4448
+ const functionRecord = record.function;
4449
+ const toolName = typeof functionRecord.name === "string" ? functionRecord.name.trim() : "";
4450
+ if (!toolName) return null;
4451
+ const callId = rawId || `${toolName}-${fallbackIndex + 1}`;
4452
+ return {
4453
+ match: segment.raw,
4454
+ groups: [callId, toolName, stringifyToolArgs((_b = functionRecord.arguments) != null ? _b : {}), serviceTag],
4455
+ toolName,
4456
+ callId,
4457
+ serviceTag,
4458
+ start: segment.start,
4459
+ end: segment.end
4460
+ };
4461
+ }
4462
+ if (isObjectRecord(record.functionCall) && typeof record.functionCall.name === "string") {
4463
+ const functionCall = record.functionCall;
4464
+ const toolName = String(functionCall.name).trim();
4465
+ if (!toolName) return null;
4466
+ const callId = rawId || `${toolName}-${fallbackIndex + 1}`;
4467
+ return {
4468
+ match: segment.raw,
4469
+ groups: [callId, toolName, stringifyToolArgs((_c = functionCall.args) != null ? _c : {}), serviceTag],
4470
+ toolName,
4471
+ callId,
4472
+ serviceTag,
4473
+ start: segment.start,
4474
+ end: segment.end
4475
+ };
4476
+ }
4477
+ return null;
4478
+ };
4479
+ var extractBalancedObjectCandidate = (input) => {
4480
+ const text = typeof input === "string" ? input : "";
4481
+ const start = text.indexOf("{");
4482
+ if (start === -1) return null;
4483
+ let depth = 0;
4484
+ let inString = false;
4485
+ let escaped = false;
4486
+ for (let index = start; index < text.length; index += 1) {
4487
+ const char = text[index];
4488
+ if (inString) {
4489
+ if (escaped) {
4490
+ escaped = false;
4491
+ continue;
4492
+ }
4493
+ if (char === "\\") {
4494
+ escaped = true;
4495
+ continue;
4496
+ }
4497
+ if (char === '"') {
4498
+ inString = false;
4499
+ }
4500
+ continue;
4501
+ }
4502
+ if (char === '"') {
4503
+ inString = true;
4504
+ continue;
4505
+ }
4506
+ if (char === "{") {
4507
+ depth += 1;
4508
+ continue;
4509
+ }
4510
+ if (char === "}") {
4511
+ depth -= 1;
4512
+ if (depth === 0) {
4513
+ return text.slice(start, index + 1);
4514
+ }
4515
+ }
4516
+ }
4517
+ return null;
4518
+ };
4519
+ var extractLineLevelFallbackToolRequests = (source, existing) => {
4520
+ const text = typeof source === "string" ? source : "";
4521
+ if (!text) return [];
4522
+ const takenRanges = existing.map((request) => ({
4523
+ start: request.start,
4524
+ end: request.end
4525
+ }));
4526
+ const overlapsExisting = (start, end) => takenRanges.some((range) => start < range.end && end > range.start);
4527
+ const requests = [];
4528
+ let offset = 0;
4529
+ const lines = text.split("\n");
4530
+ const normalizeStandaloneToolJsonLine = (line) => {
4531
+ const trimmed = line.trim();
4532
+ if (!trimmed) return null;
4533
+ let normalized = trimmed;
4534
+ const first = normalized[0];
4535
+ const last = normalized[normalized.length - 1];
4536
+ const hasSymmetricQuote = normalized.length >= 2 && (first === "'" && last === "'" || first === '"' && last === '"' || first === "`" && last === "`");
4537
+ if (hasSymmetricQuote) {
4538
+ const inner = normalized.slice(1, -1).trim();
4539
+ if (first === '"') {
4540
+ try {
4541
+ const decoded = JSON.parse(normalized);
4542
+ if (typeof decoded === "string") {
4543
+ normalized = decoded.trim();
4544
+ } else {
4545
+ normalized = inner;
4546
+ }
4547
+ } catch (_error) {
4548
+ normalized = inner;
4549
+ }
4550
+ } else {
4551
+ normalized = inner;
4552
+ }
4553
+ }
4554
+ const extractedCandidate = extractBalancedObjectCandidate(normalized);
4555
+ if (extractedCandidate) {
4556
+ normalized = extractedCandidate.trim();
4557
+ }
4558
+ if (!normalized.startsWith("{")) return null;
4559
+ if (!normalized.includes('"type"')) return null;
4560
+ return normalized;
4561
+ };
4562
+ const extractBalancedObjectAfterField = (input, fieldName) => {
4563
+ const fieldRegex = new RegExp(`"${fieldName}"\\s*:\\s*`, "i");
4564
+ const fieldMatch = fieldRegex.exec(input);
4565
+ if (!fieldMatch) return null;
4566
+ const startIndex = input.indexOf("{", fieldMatch.index + fieldMatch[0].length);
4567
+ if (startIndex === -1) return null;
4568
+ let depth = 0;
4569
+ let inString = false;
4570
+ let escaped = false;
4571
+ for (let index = startIndex; index < input.length; index += 1) {
4572
+ const char = input[index];
4573
+ if (inString) {
4574
+ if (escaped) {
4575
+ escaped = false;
4576
+ continue;
4577
+ }
4578
+ if (char === "\\") {
4579
+ escaped = true;
4580
+ continue;
4581
+ }
4582
+ if (char === '"') {
4583
+ inString = false;
4584
+ }
4585
+ continue;
4586
+ }
4587
+ if (char === '"') {
4588
+ inString = true;
4589
+ continue;
4590
+ }
4591
+ if (char === "{") {
4592
+ depth += 1;
4593
+ continue;
4594
+ }
4595
+ if (char === "}") {
4596
+ depth -= 1;
4597
+ if (depth === 0) {
4598
+ return input.slice(startIndex, index + 1);
4599
+ }
4600
+ }
4601
+ }
4602
+ return null;
4603
+ };
4604
+ lines.forEach((line, index) => {
4605
+ var _a, _b, _c, _d, _e, _f;
4606
+ const lineStart = offset;
4607
+ const lineEnd = lineStart + line.length;
4608
+ offset = lineEnd + (index < lines.length - 1 ? 1 : 0);
4609
+ const normalized = normalizeStandaloneToolJsonLine(line);
4610
+ if (!normalized) return;
4611
+ if (!(normalized.includes('"tool_use"') || normalized.includes('"function"'))) return;
4612
+ if (/(^|[\r\n])\s*[A-Za-z][A-Za-z0-9 _-]{0,30}:\s*\{/.test(line)) return;
4613
+ if (overlapsExisting(lineStart, lineEnd)) return;
4614
+ try {
4615
+ const parsedLine = JSON.parse(normalized);
4616
+ if (isObjectRecord(parsedLine)) {
4617
+ const type2 = typeof parsedLine.type === "string" ? parsedLine.type.trim().toLowerCase() : "";
4618
+ if (type2 === "tool_use") {
4619
+ const toolName2 = typeof parsedLine.name === "string" ? parsedLine.name.trim() : "";
4620
+ if (!toolName2) return;
4621
+ const callId2 = typeof parsedLine.id === "string" && parsedLine.id.trim().length > 0 ? parsedLine.id.trim() : `tool-call-${index + 1}`;
4622
+ const serviceTag = typeof parsedLine.service === "string" ? parsedLine.service : "";
4623
+ const parsedArgs2 = parseToolArguments((_a = parsedLine.input) != null ? _a : {});
4624
+ if (!parsedArgs2) return;
4625
+ requests.push({
4626
+ match: line,
4627
+ groups: [callId2, toolName2, stringifyToolArgs(parsedArgs2), serviceTag],
4628
+ toolName: toolName2,
4629
+ callId: callId2,
4630
+ serviceTag,
4631
+ start: lineStart,
4632
+ end: lineEnd
4633
+ });
4634
+ return;
4635
+ }
4636
+ if (type2 === "function" && isObjectRecord(parsedLine.function)) {
4637
+ const functionRecord = parsedLine.function;
4638
+ const toolName2 = typeof functionRecord.name === "string" ? functionRecord.name.trim() : "";
4639
+ if (!toolName2) return;
4640
+ const callId2 = typeof parsedLine.id === "string" && parsedLine.id.trim().length > 0 ? parsedLine.id.trim() : `tool-call-${index + 1}`;
4641
+ const serviceTag = typeof parsedLine.service === "string" ? parsedLine.service : "";
4642
+ const parsedArgs2 = parseToolArguments((_b = functionRecord.arguments) != null ? _b : {});
4643
+ if (!parsedArgs2) return;
4644
+ requests.push({
4645
+ match: line,
4646
+ groups: [callId2, toolName2, stringifyToolArgs(parsedArgs2), serviceTag],
4647
+ toolName: toolName2,
4648
+ callId: callId2,
4649
+ serviceTag,
4650
+ start: lineStart,
4651
+ end: lineEnd
4652
+ });
4653
+ return;
4654
+ }
4655
+ }
4656
+ } catch (_error) {
4657
+ }
4658
+ const typeMatch = normalized.match(/"type"\s*:\s*"([^"]+)"/i);
4659
+ const type = (_c = typeMatch == null ? void 0 : typeMatch[1]) == null ? void 0 : _c.trim().toLowerCase();
4660
+ if (type !== "tool_use" && type !== "function") return;
4661
+ const idMatch = normalized.match(/"id"\s*:\s*"([^"]+)"/i);
4662
+ const callId = ((_d = idMatch == null ? void 0 : idMatch[1]) == null ? void 0 : _d.trim()) || `tool-call-${index + 1}`;
4663
+ let toolName = "";
4664
+ let argsRaw = "{}";
4665
+ if (type === "tool_use") {
4666
+ const nameMatch = normalized.match(/"name"\s*:\s*"([^"]+)"/i);
4667
+ toolName = ((_e = nameMatch == null ? void 0 : nameMatch[1]) == null ? void 0 : _e.trim()) || "";
4668
+ const inputObject = extractBalancedObjectAfterField(normalized, "input");
4669
+ if (inputObject) {
4670
+ argsRaw = inputObject;
4671
+ }
4672
+ } else {
4673
+ const fnNameMatch = normalized.match(/"function"\s*:\s*\{[\s\S]*?"name"\s*:\s*"([^"]+)"/i);
4674
+ toolName = ((_f = fnNameMatch == null ? void 0 : fnNameMatch[1]) == null ? void 0 : _f.trim()) || "";
4675
+ const argumentsObject = extractBalancedObjectAfterField(normalized, "arguments");
4676
+ if (argumentsObject) {
4677
+ argsRaw = argumentsObject;
4678
+ } else {
4679
+ const argsStringMatch = normalized.match(/"arguments"\s*:\s*"([\s\S]*?)"(?:\s*,|\s*})/i);
4680
+ if (argsStringMatch == null ? void 0 : argsStringMatch[1]) {
4681
+ const rawArgs = argsStringMatch[1];
4682
+ try {
4683
+ argsRaw = JSON.parse(`"${rawArgs}"`);
4684
+ } catch (_error) {
4685
+ argsRaw = rawArgs;
4686
+ }
4687
+ }
4688
+ }
4689
+ }
4690
+ if (!toolName) return;
4691
+ const parsedArgs = parseToolArguments(argsRaw);
4692
+ if (!parsedArgs) return;
4693
+ requests.push({
4694
+ match: line,
4695
+ groups: [callId, toolName, stringifyToolArgs(parsedArgs), ""],
4696
+ toolName,
4697
+ callId,
4698
+ serviceTag: "",
4699
+ start: lineStart,
4700
+ end: lineEnd
4701
+ });
4702
+ });
4703
+ return requests;
4704
+ };
4705
+ var stripStandaloneRawToolJsonLines = (source) => {
4706
+ const text = typeof source === "string" ? source : "";
4707
+ if (!text) return text;
4708
+ const normalizeStandaloneToolJsonLine = (line) => {
4709
+ const trimmed = line.trim();
4710
+ if (!trimmed) return null;
4711
+ let normalized = trimmed;
4712
+ const first = normalized[0];
4713
+ const last = normalized[normalized.length - 1];
4714
+ const hasSymmetricQuote = normalized.length >= 2 && (first === "'" && last === "'" || first === '"' && last === '"' || first === "`" && last === "`");
4715
+ if (hasSymmetricQuote) {
4716
+ const inner = normalized.slice(1, -1).trim();
4717
+ if (first === '"') {
4718
+ try {
4719
+ const decoded = JSON.parse(normalized);
4720
+ if (typeof decoded === "string") {
4721
+ normalized = decoded.trim();
4722
+ } else {
4723
+ normalized = inner;
4724
+ }
4725
+ } catch (_error) {
4726
+ normalized = inner;
4727
+ }
4728
+ } else {
4729
+ normalized = inner;
4730
+ }
4731
+ }
4732
+ const extractedCandidate = extractBalancedObjectCandidate(normalized);
4733
+ if (extractedCandidate) {
4734
+ normalized = extractedCandidate.trim();
4735
+ }
4736
+ if (!normalized.startsWith("{") || !normalized.endsWith("}")) return null;
4737
+ return normalized;
4738
+ };
4739
+ const lines = text.split("\n");
4740
+ let removedAny = false;
4741
+ const kept = lines.filter((line) => {
4742
+ const normalized = normalizeStandaloneToolJsonLine(line);
4743
+ if (!normalized) return true;
4744
+ const hasToolType = /"type"\s*:\s*"(tool_use|function)"/i.test(normalized);
4745
+ if (!hasToolType) return true;
4746
+ const hasName = /"name"\s*:\s*"[^"]+"/i.test(normalized);
4747
+ const hasFunctionName = /"function"\s*:\s*\{[\s\S]*?"name"\s*:\s*"[^"]+"/i.test(normalized);
4748
+ if (!hasName && !hasFunctionName) return true;
4749
+ removedAny = true;
4750
+ return false;
4751
+ });
4752
+ if (!removedAny) return text;
4753
+ return kept.join("\n").replace(/\n{3,}/g, "\n\n");
4754
+ };
4755
+ var extractToolRequestMatchesFromText = (rawResponse) => {
4756
+ const requests = [];
4757
+ const segments = extractTopLevelJsonObjectSegments(rawResponse);
4758
+ segments.forEach((segment, segmentIndex) => {
4759
+ const parsed = resolveToolRequestFromSegment(segment, segmentIndex);
4760
+ if (!parsed) return;
4761
+ requests.push(parsed);
4762
+ });
4763
+ const fallbackRequests = extractLineLevelFallbackToolRequests(rawResponse, requests);
4764
+ fallbackRequests.forEach((request) => {
4765
+ requests.push(request);
4766
+ });
4767
+ if (requests.length === 0) return [];
4768
+ requests.sort((a, b) => a.start - b.start);
4769
+ const seenSignatures = /* @__PURE__ */ new Set();
4770
+ const deduped = requests.filter((request) => {
4771
+ const signature = `${String(request.toolName || "").trim().toLowerCase()}::${String(
4772
+ request.callId || ""
4773
+ ).trim().toLowerCase()}::${request.start}`;
4774
+ if (seenSignatures.has(signature)) return false;
4775
+ seenSignatures.add(signature);
4776
+ return true;
4777
+ });
4778
+ deduped.sort((a, b) => a.start - b.start);
4779
+ return deduped;
4780
+ };
4781
+ var hashInlineMarkerValue = (value) => {
4782
+ let hash = 5381;
4783
+ const input = String(value || "");
4784
+ for (let index = 0; index < input.length; index += 1) {
4785
+ hash = (hash << 5) + hash ^ input.charCodeAt(index);
4786
+ }
4787
+ return (hash >>> 0).toString(36);
4788
+ };
4789
+ var getThinkingBlockSignature = (type, content) => {
4790
+ const normalizedContent = String(content || "").trim();
4791
+ return `${type}-${hashInlineMarkerValue(normalizedContent)}-${normalizedContent.length}`;
4792
+ };
4793
+ var buildThinkingBlockMarker = (type, signature) => {
4794
+ const normalizedType = String(type || "thinking").trim();
4795
+ const normalizedSignature = String(signature || "").trim() || `${normalizedType}-block`;
4796
+ return `${INLINE_THINKING_MARKER_PREFIX}${encodeURIComponent(normalizedType)}|${encodeURIComponent(
4797
+ normalizedSignature
4798
+ )}${INLINE_THINKING_MARKER_SUFFIX}`;
4799
+ };
4800
+ var buildInlineToolMarker = (toolName, callId) => {
4801
+ const normalizedToolName = String(toolName || "").trim() || "tool";
4802
+ const normalizedCallId = String(callId || "").trim() || `${normalizedToolName}-call`;
4803
+ return `${INLINE_TOOL_MARKER_PREFIX}${encodeURIComponent(normalizedToolName)}|${encodeURIComponent(
4804
+ normalizedCallId
4805
+ )}${INLINE_TOOL_MARKER_SUFFIX}`;
4806
+ };
4807
+ var parseInlineToolMarkers = (text) => {
4808
+ const source = typeof text === "string" ? text : "";
4809
+ if (!source) return { parts: [""], markers: [] };
4810
+ const parts = [];
4811
+ const markers = [];
4812
+ let lastIndex = 0;
4813
+ INLINE_TOOL_MARKER_REGEX.lastIndex = 0;
4814
+ let match;
4815
+ while ((match = INLINE_TOOL_MARKER_REGEX.exec(source)) !== null) {
4816
+ parts.push(source.slice(lastIndex, match.index));
4817
+ const encodedToolName = match[1] || "";
4818
+ const encodedCallId = match[2] || "";
4819
+ let toolName = encodedToolName;
4820
+ let callId = encodedCallId;
4821
+ try {
4822
+ toolName = decodeURIComponent(encodedToolName);
4823
+ } catch (_error) {
4824
+ toolName = encodedToolName;
4825
+ }
4826
+ try {
4827
+ callId = decodeURIComponent(encodedCallId);
4828
+ } catch (_error) {
4829
+ callId = encodedCallId;
4830
+ }
4831
+ markers.push({
4832
+ toolName: String(toolName || "").trim() || "tool",
4833
+ callId: String(callId || "").trim() || "call"
4834
+ });
4835
+ lastIndex = match.index + match[0].length;
4836
+ }
4837
+ parts.push(source.slice(lastIndex));
4838
+ return { parts, markers };
4839
+ };
4840
+ var parseInlineThinkingMarkers = (text) => {
4841
+ const source = typeof text === "string" ? text : "";
4842
+ if (!source) return { parts: [""], markers: [] };
4843
+ const parts = [];
4844
+ const markers = [];
4845
+ let lastIndex = 0;
4846
+ INLINE_THINKING_MARKER_REGEX.lastIndex = 0;
4847
+ let match;
4848
+ while ((match = INLINE_THINKING_MARKER_REGEX.exec(source)) !== null) {
4849
+ parts.push(source.slice(lastIndex, match.index));
4850
+ const encodedType = match[1] || "";
4851
+ const encodedSignature = match[2] || "";
4852
+ let rawType = encodedType;
4853
+ let signature = encodedSignature;
4854
+ try {
4855
+ rawType = decodeURIComponent(encodedType);
4856
+ } catch (_error) {
4857
+ rawType = encodedType;
4858
+ }
4859
+ try {
4860
+ signature = decodeURIComponent(encodedSignature);
4861
+ } catch (_error) {
4862
+ signature = encodedSignature;
4863
+ }
4864
+ const normalizedType = String(rawType || "").trim().toLowerCase();
4865
+ const type = normalizedType === "reasoning" ? "reasoning" : normalizedType === "searching" ? "searching" : "thinking";
4866
+ markers.push({
4867
+ type,
4868
+ signature: String(signature || "").trim()
4869
+ });
4870
+ lastIndex = match.index + match[0].length;
4871
+ }
4872
+ parts.push(source.slice(lastIndex));
4873
+ return { parts, markers };
4874
+ };
3771
4875
  var ArrowUpIcon = () => /* @__PURE__ */ import_react14.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "ai-chat-icon-sm" }, /* @__PURE__ */ import_react14.default.createElement("path", { d: "M12 19V5M5 12l7-7 7 7" }));
3772
4876
  var StopIcon = () => /* @__PURE__ */ import_react14.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", className: "ai-chat-icon" }, /* @__PURE__ */ import_react14.default.createElement("rect", { x: "6", y: "6", width: "12", height: "12", rx: "2" }));
3773
4877
  var ChevronDownIcon = () => /* @__PURE__ */ import_react14.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "ai-chat-icon-sm" }, /* @__PURE__ */ import_react14.default.createElement("path", { d: "m6 9 6 6 6-6" }));
3774
4878
  var ChevronUpIcon = () => /* @__PURE__ */ import_react14.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "ai-chat-icon-sm" }, /* @__PURE__ */ import_react14.default.createElement("path", { d: "m18 15-6-6-6 6" }));
3775
4879
  var AgentIcon = () => /* @__PURE__ */ import_react14.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "ai-chat-icon-sm" }, /* @__PURE__ */ import_react14.default.createElement("path", { d: "M12 8V4H8" }), /* @__PURE__ */ import_react14.default.createElement("rect", { width: "16", height: "12", x: "4", y: "8", rx: "2" }), /* @__PURE__ */ import_react14.default.createElement("path", { d: "M2 14h2" }), /* @__PURE__ */ import_react14.default.createElement("path", { d: "M20 14h2" }), /* @__PURE__ */ import_react14.default.createElement("path", { d: "M15 13v2" }), /* @__PURE__ */ import_react14.default.createElement("path", { d: "M9 13v2" }));
4880
+ var ToolIcon = () => /* @__PURE__ */ import_react14.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "ai-chat-icon-sm" }, /* @__PURE__ */ import_react14.default.createElement("path", { d: "M14.7 6.3a4 4 0 0 0-5.4 5.4l-6 6a2 2 0 0 0 2.8 2.8l6-6a4 4 0 0 0 5.4-5.4l-2.1 2.1-3.3-3.3 2.6-1.6Z" }));
3776
4881
  var CheckIcon = () => /* @__PURE__ */ import_react14.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "ai-chat-icon-sm" }, /* @__PURE__ */ import_react14.default.createElement("polyline", { points: "20 6 9 17 4 12" }));
3777
4882
  var LLMAsAServiceLogo = () => /* @__PURE__ */ import_react14.default.createElement("svg", { width: "16", height: "16", viewBox: "0 0 72 72", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ import_react14.default.createElement("ellipse", { cx: "14.0868", cy: "59.2146", rx: "7.8261", ry: "7.7854", fill: "#2487D8" }), /* @__PURE__ */ import_react14.default.createElement("ellipse", { cx: "24.9013", cy: "43.0776", rx: "6.11858", ry: "6.08676", fill: "#2487D8" }), /* @__PURE__ */ import_react14.default.createElement("ellipse", { cx: "45.391", cy: "43.0776", rx: "6.11858", ry: "6.08676", fill: "#2487D8" }), /* @__PURE__ */ import_react14.default.createElement("ellipse", { cx: "65.8813", cy: "43.0776", rx: "6.11858", ry: "6.08676", fill: "#2487D8" }), /* @__PURE__ */ import_react14.default.createElement("ellipse", { cx: "35.1461", cy: "26.5327", rx: "4.41103", ry: "4.3878", fill: "#2487D8" }), /* @__PURE__ */ import_react14.default.createElement("ellipse", { cx: "55.6364", cy: "26.5327", rx: "4.41103", ry: "4.3878", fill: "#2487D8" }), /* @__PURE__ */ import_react14.default.createElement("ellipse", { cx: "45.391", cy: "10.3959", rx: "2.70351", ry: "2.68919", fill: "#2487D8" }));
3778
4883
  var AlertCircleIcon = () => /* @__PURE__ */ import_react14.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "ai-chat-icon-sm" }, /* @__PURE__ */ import_react14.default.createElement("circle", { cx: "12", cy: "12", r: "10" }), /* @__PURE__ */ import_react14.default.createElement("line", { x1: "12", x2: "12", y1: "8", y2: "12" }), /* @__PURE__ */ import_react14.default.createElement("line", { x1: "12", x2: "12.01", y1: "16", y2: "16" }));
3779
4884
  var CloseIcon = () => /* @__PURE__ */ import_react14.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "ai-chat-icon-sm" }, /* @__PURE__ */ import_react14.default.createElement("line", { x1: "18", x2: "6", y1: "6", y2: "18" }), /* @__PURE__ */ import_react14.default.createElement("line", { x1: "6", x2: "18", y1: "6", y2: "18" }));
3780
4885
  var ChatInput = import_react14.default.memo(({
3781
4886
  placeholder,
3782
- idle,
4887
+ isBusy,
3783
4888
  onSubmit,
3784
4889
  onStop,
3785
4890
  agentOptions,
@@ -3812,14 +4917,14 @@ var ChatInput = import_react14.default.memo(({
3812
4917
  }, []);
3813
4918
  const handleSubmit = (0, import_react14.useCallback)(() => {
3814
4919
  const trimmed = inputValue.trim();
3815
- if (trimmed && idle) {
4920
+ if (trimmed && !isBusy) {
3816
4921
  onSubmit(trimmed);
3817
4922
  setInputValue("");
3818
4923
  if (textareaRef.current) {
3819
4924
  textareaRef.current.style.height = "auto";
3820
4925
  }
3821
4926
  }
3822
- }, [inputValue, idle, onSubmit]);
4927
+ }, [inputValue, isBusy, onSubmit]);
3823
4928
  (0, import_react14.useEffect)(() => {
3824
4929
  const handleClickOutside = (event) => {
3825
4930
  if (containerRef.current && !containerRef.current.contains(event.target)) {
@@ -3855,9 +4960,6 @@ var ChatInput = import_react14.default.memo(({
3855
4960
  const hasRawData2 = (section) => {
3856
4961
  return typeof section.rawData === "string";
3857
4962
  };
3858
- const hasStructuredData = (section) => {
3859
- return typeof section.data === "object" && section.data !== null;
3860
- };
3861
4963
  const estimateSectionTokens2 = (section) => {
3862
4964
  var _a;
3863
4965
  if (typeof section.tokens === "number") {
@@ -3868,32 +4970,6 @@ var ChatInput = import_react14.default.memo(({
3868
4970
  }
3869
4971
  return Math.ceil(JSON.stringify((_a = section.data) != null ? _a : {}).length / 4);
3870
4972
  };
3871
- const detectFormat = (section) => {
3872
- if (section.format) {
3873
- return section.format;
3874
- }
3875
- if (!hasRawData2(section)) {
3876
- return "json";
3877
- }
3878
- const raw = section.rawData.trim();
3879
- if (raw.startsWith("{") || raw.startsWith("[")) return "json";
3880
- if (raw.includes("# ") || raw.includes("```")) return "markdown";
3881
- return "text";
3882
- };
3883
- const formatBadge = (format) => {
3884
- if (format === "toon") return "TOON";
3885
- if (format === "markdown") return "MD";
3886
- if (format === "text") return "TEXT";
3887
- return "JSON";
3888
- };
3889
- const [detailViewModeBySection, setDetailViewModeBySection] = (0, import_react14.useState)({});
3890
- const getVisibleDetailMode = (section) => {
3891
- var _a;
3892
- if (hasRawData2(section)) {
3893
- return (_a = detailViewModeBySection[section.id]) != null ? _a : "raw";
3894
- }
3895
- return "structured";
3896
- };
3897
4973
  const getStructuredContent = (section) => {
3898
4974
  var _a;
3899
4975
  return JSON.stringify((_a = section.data) != null ? _a : {}, null, 2);
@@ -3985,10 +5061,10 @@ var ChatInput = import_react14.default.memo(({
3985
5061
  className: `ai-chat-context-popover__progress-bar ${isOverLimit ? "ai-chat-context-popover__progress-bar--warning" : ""}`,
3986
5062
  style: { width: `${Math.min(tokenPercentage, 100)}%` }
3987
5063
  }
3988
- ))), /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-context-popover__sections" }, contextSections.map((section) => /* @__PURE__ */ import_react14.default.createElement(
5064
+ ))), /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-context-popover__sections" }, contextSections.map((section, index) => /* @__PURE__ */ import_react14.default.createElement(
3989
5065
  "div",
3990
5066
  {
3991
- key: section.id,
5067
+ key: `${section.id}-${index}`,
3992
5068
  className: `ai-chat-context-popover__section-item ${enableContextDetailView ? "ai-chat-context-popover__section-item--clickable" : ""}`,
3993
5069
  onClick: () => {
3994
5070
  if (enableContextDetailView) {
@@ -3999,7 +5075,6 @@ var ChatInput = import_react14.default.memo(({
3999
5075
  },
4000
5076
  /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-context-popover__section-icon" }, "\u{1F4C4}"),
4001
5077
  /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-context-popover__section-title" }, section.title),
4002
- /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-context-popover__section-format" }, formatBadge(detectFormat(section))),
4003
5078
  /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-context-popover__section-tokens" }, estimateSectionTokens2(section))
4004
5079
  ))), enableContextDetailView && /* @__PURE__ */ import_react14.default.createElement(
4005
5080
  "button",
@@ -4041,15 +5116,13 @@ var ChatInput = import_react14.default.memo(({
4041
5116
  className: `ai-chat-context-popover__progress-bar ${isOverLimit ? "ai-chat-context-popover__progress-bar--warning" : ""}`,
4042
5117
  style: { width: `${Math.min(tokenPercentage, 100)}%` }
4043
5118
  }
4044
- ))), /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-context-popover__detail-sections" }, contextSections.map((section) => {
5119
+ ))), /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-context-popover__detail-sections" }, contextSections.map((section, index) => {
4045
5120
  const isRawSection = hasRawData2(section);
4046
- const hasBothViews = isRawSection && hasStructuredData(section);
4047
- const visibleMode = getVisibleDetailMode(section);
4048
5121
  const isEnabled = !disabledSectionIds.has(section.id);
4049
5122
  return /* @__PURE__ */ import_react14.default.createElement(
4050
5123
  "details",
4051
5124
  {
4052
- key: section.id,
5125
+ key: `${section.id}-${index}`,
4053
5126
  className: `ai-chat-context-popover__detail-section ${!isEnabled ? "ai-chat-context-popover__detail-section--disabled" : ""}`,
4054
5127
  open: expandedSectionId === section.id
4055
5128
  },
@@ -4075,43 +5148,18 @@ var ChatInput = import_react14.default.memo(({
4075
5148
  }
4076
5149
  ),
4077
5150
  /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-context-toggle__slider" })
4078
- )), /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-context-popover__detail-section-meta" }, /* @__PURE__ */ import_react14.default.createElement("code", null, `{{${section.id}}}`), /* @__PURE__ */ import_react14.default.createElement("span", null, formatBadge(detectFormat(section))), /* @__PURE__ */ import_react14.default.createElement("span", null, "~", estimateSectionTokens2(section)))),
4079
- hasBothViews && /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-context-popover__detail-view-toggle" }, /* @__PURE__ */ import_react14.default.createElement(
4080
- "button",
4081
- {
4082
- type: "button",
4083
- className: `ai-chat-context-popover__detail-view-button ${visibleMode === "raw" ? "active" : ""}`,
4084
- onClick: (e) => {
4085
- e.preventDefault();
4086
- e.stopPropagation();
4087
- setDetailViewModeBySection((prev) => __spreadProps(__spreadValues({}, prev), { [section.id]: "raw" }));
4088
- }
4089
- },
4090
- "Raw"
4091
- ), /* @__PURE__ */ import_react14.default.createElement(
4092
- "button",
4093
- {
4094
- type: "button",
4095
- className: `ai-chat-context-popover__detail-view-button ${visibleMode === "structured" ? "active" : ""}`,
4096
- onClick: (e) => {
4097
- e.preventDefault();
4098
- e.stopPropagation();
4099
- setDetailViewModeBySection((prev) => __spreadProps(__spreadValues({}, prev), { [section.id]: "structured" }));
4100
- }
4101
- },
4102
- "Structured"
4103
- )),
4104
- /* @__PURE__ */ import_react14.default.createElement("pre", { className: "ai-chat-context-popover__detail-content" }, /* @__PURE__ */ import_react14.default.createElement("code", null, visibleMode === "raw" && isRawSection ? section.rawData : getStructuredContent(section)))
5151
+ )), /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-context-popover__detail-section-meta" }, /* @__PURE__ */ import_react14.default.createElement("code", null, `{{${section.id}}}`), /* @__PURE__ */ import_react14.default.createElement("span", null, "~", estimateSectionTokens2(section)))),
5152
+ /* @__PURE__ */ import_react14.default.createElement("pre", { className: "ai-chat-context-popover__detail-content" }, /* @__PURE__ */ import_react14.default.createElement("code", null, isRawSection ? section.rawData : getStructuredContent(section)))
4105
5153
  );
4106
5154
  })))
4107
5155
  )), /* @__PURE__ */ import_react14.default.createElement(
4108
5156
  "button",
4109
5157
  {
4110
- className: `ai-chat-send-button ${idle && !inputValue.trim() ? "ai-chat-send-button--disabled" : ""} ${!idle ? "ai-chat-send-button--stop" : ""}`,
4111
- onClick: () => idle ? handleSubmit() : onStop(),
4112
- disabled: idle && !inputValue.trim()
5158
+ className: `ai-chat-send-button ${!isBusy && !inputValue.trim() ? "ai-chat-send-button--disabled" : ""} ${isBusy ? "ai-chat-send-button--stop" : ""}`,
5159
+ onClick: () => isBusy ? onStop() : handleSubmit(),
5160
+ disabled: !isBusy && !inputValue.trim()
4113
5161
  },
4114
- idle ? /* @__PURE__ */ import_react14.default.createElement(ArrowUpIcon, null) : /* @__PURE__ */ import_react14.default.createElement(StopIcon, null)
5162
+ isBusy ? /* @__PURE__ */ import_react14.default.createElement(StopIcon, null) : /* @__PURE__ */ import_react14.default.createElement(ArrowUpIcon, null)
4115
5163
  )),
4116
5164
  agentOptions.length > 0 && dropdownOpen && /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-agent-selector__dropdown-inside" }, agentOptions.map((option) => /* @__PURE__ */ import_react14.default.createElement(
4117
5165
  "button",
@@ -4168,7 +5216,11 @@ var AIChatPanel = ({
4168
5216
  initialHistory = {},
4169
5217
  hideRagContextInPrompt = true,
4170
5218
  createConversationOnFirstChat = true,
5219
+ autoApproveTools = false,
4171
5220
  mcpServers = [],
5221
+ resolveMcpAuthHeaders,
5222
+ localToolExecutors,
5223
+ traceContextMode = "standard",
4172
5224
  progressiveActions = true,
4173
5225
  agentOptions = [],
4174
5226
  currentAgentId,
@@ -4181,6 +5233,7 @@ var AIChatPanel = ({
4181
5233
  disabledSectionIds: propDisabledSectionIds,
4182
5234
  onToggleSection: propOnToggleSection,
4183
5235
  onConversationCreated,
5236
+ onBeforeSend,
4184
5237
  // UI Customization Props
4185
5238
  cssUrl,
4186
5239
  markdownClass,
@@ -4216,6 +5269,8 @@ var AIChatPanel = ({
4216
5269
  const [collapsedBlocks, setCollapsedBlocks] = (0, import_react14.useState)(/* @__PURE__ */ new Set());
4217
5270
  const hasAutoCollapsedRef = (0, import_react14.useRef)(false);
4218
5271
  const prevBlockCountRef = (0, import_react14.useRef)(0);
5272
+ const thinkingBlocksByKeyRef = (0, import_react14.useRef)({});
5273
+ const [thinkingBlocksByKey, setThinkingBlocksByKey] = (0, import_react14.useState)({});
4219
5274
  const [newConversationConfirm, setNewConversationConfirm] = (0, import_react14.useState)(false);
4220
5275
  const [justReset, setJustReset] = (0, import_react14.useState)(false);
4221
5276
  const [copiedCallId, setCopiedCallId] = (0, import_react14.useState)(null);
@@ -4235,8 +5290,64 @@ var AIChatPanel = ({
4235
5290
  const [pendingToolRequests, setPendingToolRequests] = (0, import_react14.useState)([]);
4236
5291
  const [sessionApprovedTools, setSessionApprovedTools] = (0, import_react14.useState)([]);
4237
5292
  const [alwaysApprovedTools, setAlwaysApprovedTools] = (0, import_react14.useState)([]);
5293
+ const [toolList, setToolList] = (0, import_react14.useState)([]);
5294
+ const [toolsLoading, setToolsLoading] = (0, import_react14.useState)(false);
5295
+ const [toolsFetchError, setToolsFetchError] = (0, import_react14.useState)(false);
5296
+ const [resolvedMcpServers, setResolvedMcpServers] = (0, import_react14.useState)(mcpServers || []);
5297
+ const [activeToolCalls, setActiveToolCalls] = (0, import_react14.useState)([]);
5298
+ const normalizeToolName = (0, import_react14.useCallback)((toolName) => {
5299
+ return String(toolName != null ? toolName : "").trim().toLowerCase();
5300
+ }, []);
5301
+ const getToolCallSignature = (0, import_react14.useCallback)(
5302
+ (toolName, callId) => {
5303
+ const normalizedToolName = normalizeToolName(toolName);
5304
+ const normalizedCallId = String(callId != null ? callId : "").trim();
5305
+ if (!normalizedToolName || !normalizedCallId) return "";
5306
+ return `${normalizedToolName}::${normalizedCallId}`;
5307
+ },
5308
+ [normalizeToolName]
5309
+ );
5310
+ const alwaysApprovedToolsStorageKey = (0, import_react14.useMemo)(() => {
5311
+ const customerId = (customer == null ? void 0 : customer.customer_id) || (customer == null ? void 0 : customer.id) || (customer == null ? void 0 : customer.customer_user_email) || "anonymous";
5312
+ const agentIdForScope = currentAgentId || agent || "default";
5313
+ return `llmasaservice-ui:always-approved-tools:${project_id}:${customerId}:${agentIdForScope}`;
5314
+ }, [project_id, customer, currentAgentId, agent]);
4238
5315
  const [internalDisabledSectionIds, setInternalDisabledSectionIds] = (0, import_react14.useState)(/* @__PURE__ */ new Set());
4239
5316
  const disabledSectionIds = propDisabledSectionIds != null ? propDisabledSectionIds : internalDisabledSectionIds;
5317
+ (0, import_react14.useEffect)(() => {
5318
+ if (typeof window === "undefined") return;
5319
+ try {
5320
+ const stored = window.localStorage.getItem(alwaysApprovedToolsStorageKey);
5321
+ if (!stored) return;
5322
+ const parsed = JSON.parse(stored);
5323
+ if (!Array.isArray(parsed)) return;
5324
+ const normalized = Array.from(
5325
+ new Set(
5326
+ parsed.map((value) => normalizeToolName(String(value))).filter((value) => value.length > 0)
5327
+ )
5328
+ );
5329
+ setAlwaysApprovedTools(normalized);
5330
+ } catch (error2) {
5331
+ console.warn("[AIChatPanel] Failed to load always-approved tools from localStorage:", error2);
5332
+ }
5333
+ }, [alwaysApprovedToolsStorageKey, normalizeToolName]);
5334
+ (0, import_react14.useEffect)(() => {
5335
+ if (typeof window === "undefined") return;
5336
+ try {
5337
+ if (alwaysApprovedTools.length === 0) {
5338
+ window.localStorage.removeItem(alwaysApprovedToolsStorageKey);
5339
+ return;
5340
+ }
5341
+ window.localStorage.setItem(
5342
+ alwaysApprovedToolsStorageKey,
5343
+ JSON.stringify(
5344
+ Array.from(new Set(alwaysApprovedTools.map((value) => normalizeToolName(value)).filter(Boolean)))
5345
+ )
5346
+ );
5347
+ } catch (error2) {
5348
+ console.warn("[AIChatPanel] Failed to persist always-approved tools to localStorage:", error2);
5349
+ }
5350
+ }, [alwaysApprovedToolsStorageKey, alwaysApprovedTools, normalizeToolName]);
4240
5351
  (0, import_react14.useEffect)(() => {
4241
5352
  setShowEmailPanel(customerEmailCaptureMode !== "HIDE");
4242
5353
  if (customerEmailCaptureMode === "REQUIRED") {
@@ -4259,6 +5370,14 @@ var AIChatPanel = ({
4259
5370
  const latestHistoryRef = (0, import_react14.useRef)(initialHistory);
4260
5371
  const initialPromptSentRef = (0, import_react14.useRef)(false);
4261
5372
  const lastFollowOnPromptRef = (0, import_react14.useRef)("");
5373
+ const handledToolCallSignaturesRef = (0, import_react14.useRef)(/* @__PURE__ */ new Set());
5374
+ const inFlightToolCallSignaturesRef = (0, import_react14.useRef)(/* @__PURE__ */ new Set());
5375
+ const toolContinuationCountRef = (0, import_react14.useRef)(0);
5376
+ const activeStreamAppendBaseRef = (0, import_react14.useRef)(null);
5377
+ const toolRequestProcessingRef = (0, import_react14.useRef)(false);
5378
+ const queuedToolRequestsRef = (0, import_react14.useRef)(null);
5379
+ const suppressAbortHistoryUpdateRef = (0, import_react14.useRef)(false);
5380
+ const toolReplaySummariesByKeyRef = (0, import_react14.useRef)({});
4262
5381
  (0, import_react14.useEffect)(() => {
4263
5382
  if (!initialHistory) return;
4264
5383
  setHistory((prev) => {
@@ -4277,10 +5396,158 @@ var AIChatPanel = ({
4277
5396
  return prev;
4278
5397
  });
4279
5398
  }, [initialHistory]);
4280
- const llmResult = (0, import_llmasaservice_client2.useLLM)(__spreadValues(__spreadValues(__spreadValues(__spreadValues({
5399
+ (0, import_react14.useEffect)(() => {
5400
+ latestHistoryRef.current = history;
5401
+ }, [history]);
5402
+ (0, import_react14.useEffect)(() => {
5403
+ let cancelled = false;
5404
+ const resolveServers = () => __async(void 0, null, function* () {
5405
+ if (!mcpServers || mcpServers.length === 0) {
5406
+ if (!cancelled) {
5407
+ setResolvedMcpServers((prev) => prev.length === 0 ? prev : []);
5408
+ }
5409
+ return;
5410
+ }
5411
+ if (!resolveMcpAuthHeaders) {
5412
+ if (!cancelled) {
5413
+ setResolvedMcpServers((prev) => {
5414
+ const hasSameServers = prev.length === mcpServers.length && prev.every((server, index) => server === mcpServers[index]);
5415
+ return hasSameServers ? prev : mcpServers;
5416
+ });
5417
+ }
5418
+ return;
5419
+ }
5420
+ try {
5421
+ const enriched = yield Promise.all(
5422
+ mcpServers.map((server) => __async(void 0, null, function* () {
5423
+ const resolved = yield resolveMcpAuthHeaders({
5424
+ phase: "list",
5425
+ mcpServer: server || {},
5426
+ projectId: project_id,
5427
+ customer
5428
+ });
5429
+ return __spreadProps(__spreadValues({}, server || {}), {
5430
+ headers: __spreadValues(__spreadValues({}, typeof (server == null ? void 0 : server.headers) === "object" && (server == null ? void 0 : server.headers) ? server.headers : {}), normalizeMcpHeaders(
5431
+ resolved
5432
+ ))
5433
+ });
5434
+ }))
5435
+ );
5436
+ if (!cancelled) setResolvedMcpServers(enriched);
5437
+ } catch (error2) {
5438
+ console.error("[AIChatPanel] Failed to resolve MCP auth headers:", error2);
5439
+ if (!cancelled) setResolvedMcpServers(mcpServers);
5440
+ }
5441
+ });
5442
+ void resolveServers();
5443
+ return () => {
5444
+ cancelled = true;
5445
+ };
5446
+ }, [mcpServers, resolveMcpAuthHeaders, project_id, customer]);
5447
+ const buildMcpRequestHeaders = (0, import_react14.useCallback)(
5448
+ (_0) => __async(void 0, [_0], function* ({
5449
+ phase,
5450
+ mcpServer,
5451
+ toolName,
5452
+ toolArgs
5453
+ }) {
5454
+ const merged = {};
5455
+ const packScopeIntoAccessToken = (headers) => {
5456
+ const accessToken = typeof headers["x-mcp-access-token"] === "string" ? headers["x-mcp-access-token"].trim() : "";
5457
+ const scopeToken = typeof headers["x-client-id"] === "string" ? headers["x-client-id"].trim() : "";
5458
+ if (!accessToken || !scopeToken) return headers;
5459
+ return __spreadProps(__spreadValues({}, headers), {
5460
+ "x-mcp-access-token": `${accessToken}::scope::${scopeToken}`
5461
+ });
5462
+ };
5463
+ const baseAccessToken = typeof mcpServer.accessToken === "string" ? mcpServer.accessToken.trim() : "";
5464
+ if (baseAccessToken) {
5465
+ merged["x-mcp-access-token"] = baseAccessToken;
5466
+ }
5467
+ if (project_id) {
5468
+ merged["x-project-id"] = project_id;
5469
+ }
5470
+ if (!resolveMcpAuthHeaders) return merged;
5471
+ try {
5472
+ const resolved = yield resolveMcpAuthHeaders({
5473
+ phase,
5474
+ mcpServer,
5475
+ projectId: project_id,
5476
+ customer,
5477
+ toolName,
5478
+ toolArgs
5479
+ });
5480
+ return packScopeIntoAccessToken(__spreadValues(__spreadValues({}, merged), normalizeMcpHeaders(
5481
+ resolved
5482
+ )));
5483
+ } catch (error2) {
5484
+ console.error(
5485
+ `Failed to resolve MCP auth headers for ${phase} request:`,
5486
+ error2
5487
+ );
5488
+ return merged;
5489
+ }
5490
+ }),
5491
+ [project_id, customer, resolveMcpAuthHeaders]
5492
+ );
5493
+ (0, import_react14.useEffect)(() => {
5494
+ const fetchAndSetTools = () => __async(void 0, null, function* () {
5495
+ if (!resolvedMcpServers || resolvedMcpServers.length === 0) {
5496
+ setToolList((prev) => prev.length === 0 ? prev : []);
5497
+ setToolsLoading((prev) => prev ? false : prev);
5498
+ setToolsFetchError((prev) => prev ? false : prev);
5499
+ return;
5500
+ }
5501
+ setToolsLoading(true);
5502
+ setToolsFetchError(false);
5503
+ try {
5504
+ const fetchPromises = (resolvedMcpServers != null ? resolvedMcpServers : []).map((m) => __async(void 0, null, function* () {
5505
+ const urlToFetch = `${publicAPIUrl}/tools/${encodeURIComponent(m.url)}`;
5506
+ const requestHeaders = yield buildMcpRequestHeaders({
5507
+ phase: "list",
5508
+ mcpServer: m
5509
+ });
5510
+ const response2 = yield fetch(urlToFetch, {
5511
+ headers: requestHeaders
5512
+ });
5513
+ if (!response2.ok) {
5514
+ const errorBody = yield response2.text();
5515
+ throw new Error(
5516
+ `HTTP ${response2.status}: ${response2.statusText} ${errorBody}`
5517
+ );
5518
+ }
5519
+ const toolsFromServer = yield response2.json();
5520
+ if (!Array.isArray(toolsFromServer)) return [];
5521
+ return toolsFromServer.map((tool) => __spreadProps(__spreadValues({}, tool), {
5522
+ url: m.url,
5523
+ accessToken: m.accessToken || "",
5524
+ headers: requestHeaders
5525
+ }));
5526
+ }));
5527
+ const results = yield Promise.all(fetchPromises);
5528
+ const allTools = results.flat();
5529
+ setToolList(allTools);
5530
+ setToolsFetchError(false);
5531
+ } catch (error2) {
5532
+ console.error("[AIChatPanel] Failed to load MCP tools:", error2);
5533
+ setToolList([]);
5534
+ setToolsFetchError(true);
5535
+ } finally {
5536
+ setToolsLoading(false);
5537
+ }
5538
+ });
5539
+ void fetchAndSetTools();
5540
+ }, [resolvedMcpServers, publicAPIUrl, buildMcpRequestHeaders]);
5541
+ const llmResult = (0, import_llmasaservice_client2.useLLM)(__spreadProps(__spreadValues(__spreadValues(__spreadValues({
4281
5542
  project_id,
4282
5543
  customer
4283
- }, url && { url }), service && { group_id: service }), agent && { agent }), mcpServers && mcpServers.length > 0 && { mcp_servers: mcpServers }));
5544
+ }, url && { url }), service && { group_id: service }), agent && { agent }), {
5545
+ tools: toolList.map((item) => ({
5546
+ name: item.name,
5547
+ description: item.description,
5548
+ parameters: item.parameters
5549
+ }))
5550
+ }));
4284
5551
  const {
4285
5552
  send,
4286
5553
  response,
@@ -4290,9 +5557,6 @@ var AIChatPanel = ({
4290
5557
  setResponse,
4291
5558
  error: llmError
4292
5559
  } = llmResult;
4293
- const toolList = llmResult.toolList || [];
4294
- const toolsLoading = llmResult.toolsLoading || false;
4295
- const toolsFetchError = llmResult.toolsFetchError || null;
4296
5560
  const historyCallbackRef = (0, import_react14.useRef)(historyChangedCallback);
4297
5561
  const responseCompleteCallbackRef = (0, import_react14.useRef)(responseCompleteCallback);
4298
5562
  const responseRef = (0, import_react14.useRef)(response);
@@ -4333,18 +5597,26 @@ var AIChatPanel = ({
4333
5597
  }
4334
5598
  }, [propOnToggleSection]);
4335
5599
  const ensureConversation = (0, import_react14.useCallback)(() => {
4336
- var _a2, _b;
5600
+ const normalizedConversationId = typeof currentConversation === "string" ? currentConversation.trim() : "";
4337
5601
  console.log("ensureConversation - called with:", {
4338
- currentConversation,
5602
+ currentConversation: normalizedConversationId || null,
4339
5603
  createConversationOnFirstChat,
4340
5604
  project_id,
4341
5605
  publicAPIUrl
4342
5606
  });
4343
- if ((!currentConversation || currentConversation === "") && createConversationOnFirstChat) {
4344
- if (!project_id) {
4345
- console.error("ensureConversation - Cannot create conversation without project_id");
4346
- return Promise.resolve("");
4347
- }
5607
+ if (normalizedConversationId) {
5608
+ console.log("ensureConversation - using existing conversation:", normalizedConversationId);
5609
+ return Promise.resolve(normalizedConversationId);
5610
+ }
5611
+ if (!createConversationOnFirstChat) {
5612
+ return Promise.resolve("");
5613
+ }
5614
+ if (!project_id) {
5615
+ console.error("ensureConversation - Cannot create conversation without project_id");
5616
+ return Promise.resolve("");
5617
+ }
5618
+ const createConversation = () => {
5619
+ var _a2, _b;
4348
5620
  const requestBody = {
4349
5621
  project_id,
4350
5622
  agentId: agent,
@@ -4370,11 +5642,13 @@ var AIChatPanel = ({
4370
5642
  }
4371
5643
  return res.json();
4372
5644
  })).then((newConvo) => {
5645
+ var _a3;
4373
5646
  console.log("ensureConversation - API response:", newConvo);
4374
- if (newConvo == null ? void 0 : newConvo.id) {
4375
- console.log("ensureConversation - New conversation ID:", newConvo.id);
4376
- setCurrentConversation(newConvo.id);
4377
- return newConvo.id;
5647
+ const createdId = typeof (newConvo == null ? void 0 : newConvo.id) === "string" && newConvo.id.trim() || typeof (newConvo == null ? void 0 : newConvo.conversationId) === "string" && newConvo.conversationId.trim() || typeof (newConvo == null ? void 0 : newConvo.conversation_id) === "string" && newConvo.conversation_id.trim() || typeof ((_a3 = newConvo == null ? void 0 : newConvo.conversation) == null ? void 0 : _a3.id) === "string" && newConvo.conversation.id.trim() || "";
5648
+ if (createdId) {
5649
+ console.log("ensureConversation - New conversation ID:", createdId);
5650
+ setCurrentConversation(createdId);
5651
+ return createdId;
4378
5652
  }
4379
5653
  console.warn("ensureConversation - No ID in response");
4380
5654
  return "";
@@ -4382,9 +5656,8 @@ var AIChatPanel = ({
4382
5656
  console.error("Error creating new conversation", error2);
4383
5657
  return "";
4384
5658
  });
4385
- }
4386
- console.log("ensureConversation - using existing conversation:", currentConversation);
4387
- return Promise.resolve(currentConversation);
5659
+ };
5660
+ return createConversation();
4388
5661
  }, [currentConversation, createConversationOnFirstChat, publicAPIUrl, project_id, agent, customer, browserInfo]);
4389
5662
  const dataWithExtras = (0, import_react14.useCallback)(() => {
4390
5663
  var _a2, _b, _c, _d, _e, _f, _g, _h;
@@ -4567,19 +5840,565 @@ var AIChatPanel = ({
4567
5840
  }
4568
5841
  return false;
4569
5842
  }, [customerEmailCaptureMode, emailInputSet]);
4570
- const handleToolApproval = (0, import_react14.useCallback)((toolName, scope) => {
4571
- if (scope === "session" || scope === "always") {
4572
- setSessionApprovedTools((prev) => Array.from(/* @__PURE__ */ new Set([...prev, toolName])));
4573
- }
4574
- if (scope === "always") {
4575
- setAlwaysApprovedTools((prev) => Array.from(/* @__PURE__ */ new Set([...prev, toolName])));
5843
+ const pendingToolRequestsRef = (0, import_react14.useRef)(pendingToolRequests);
5844
+ const streamIdleRef = (0, import_react14.useRef)(idle);
5845
+ streamIdleRef.current = idle;
5846
+ const waitForStreamIdle = (0, import_react14.useCallback)((timeoutMs = 2500) => __async(void 0, null, function* () {
5847
+ const startedAt = Date.now();
5848
+ while (!streamIdleRef.current && Date.now() - startedAt < timeoutMs) {
5849
+ yield new Promise((resolve) => {
5850
+ setTimeout(resolve, 25);
5851
+ });
4576
5852
  }
4577
- setPendingToolRequests((prev) => prev.filter((r) => r.toolName !== toolName));
4578
- console.log(`[AIChatPanel] Tool "${toolName}" approved with scope: ${scope}`);
4579
- }, []);
4580
- const getUniqueToolNames = (0, import_react14.useCallback)(() => {
4581
- return Array.from(new Set(pendingToolRequests.map((r) => r.toolName)));
5853
+ }), []);
5854
+ (0, import_react14.useEffect)(() => {
5855
+ pendingToolRequestsRef.current = pendingToolRequests;
4582
5856
  }, [pendingToolRequests]);
5857
+ const processGivenToolRequests = (0, import_react14.useCallback)(
5858
+ (requests) => __async(void 0, null, function* () {
5859
+ var _a2, _b, _c;
5860
+ const dedupeToolRequests = (input) => {
5861
+ const seen = /* @__PURE__ */ new Set();
5862
+ const deduped = [];
5863
+ for (const request of Array.isArray(input) ? input : []) {
5864
+ if (!request) continue;
5865
+ const signature = getToolCallSignature(request.toolName, request.callId) || request.match;
5866
+ if (!signature || seen.has(signature)) continue;
5867
+ seen.add(signature);
5868
+ deduped.push(request);
5869
+ }
5870
+ return deduped;
5871
+ };
5872
+ if (toolRequestProcessingRef.current) {
5873
+ const queued = dedupeToolRequests([
5874
+ ...queuedToolRequestsRef.current || [],
5875
+ ...Array.isArray(requests) ? requests : []
5876
+ ]);
5877
+ if (queued.length > 0) {
5878
+ queuedToolRequestsRef.current = queued;
5879
+ }
5880
+ return;
5881
+ }
5882
+ toolRequestProcessingRef.current = true;
5883
+ try {
5884
+ let requestsToProcess = requests;
5885
+ if (!requestsToProcess || requestsToProcess.length === 0) {
5886
+ requestsToProcess = pendingToolRequestsRef.current || [];
5887
+ }
5888
+ if (!requestsToProcess || requestsToProcess.length === 0) return;
5889
+ setIsLoading(true);
5890
+ const userPrompt = lastPromptRef.current || lastKeyRef.current || "";
5891
+ const lastPromptKey = lastKeyRef.current;
5892
+ const assistantSeedText = lastPromptKey && ((_a2 = latestHistoryRef.current[lastPromptKey]) == null ? void 0 : _a2.content) ? (_b = latestHistoryRef.current[lastPromptKey]) == null ? void 0 : _b.content : "";
5893
+ const historyForContinuation = latestHistoryRef.current || {};
5894
+ const newMessages = [];
5895
+ Object.entries(historyForContinuation).forEach(([historyPrompt, historyEntry]) => {
5896
+ let promptForHistory = String(historyPrompt || "");
5897
+ const isoTimestampRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z:/;
5898
+ if (isoTimestampRegex.test(promptForHistory)) {
5899
+ const colonIndex = promptForHistory.indexOf(":", 19);
5900
+ promptForHistory = promptForHistory.substring(colonIndex + 1);
5901
+ } else if (/^\d+:/.test(promptForHistory)) {
5902
+ const colonIndex = promptForHistory.indexOf(":");
5903
+ promptForHistory = promptForHistory.substring(colonIndex + 1);
5904
+ }
5905
+ const typedHistoryEntry = historyEntry || { content: "", callId: "" };
5906
+ const assistantBaseContent = typeof typedHistoryEntry.content === "string" ? typedHistoryEntry.content : "";
5907
+ let assistantContextContent = assistantBaseContent;
5908
+ if (traceContextMode === "full") {
5909
+ const traceSummary = buildCompactTraceSummary({
5910
+ reasoningBlocks: thinkingBlocksByKeyRef.current[historyPrompt] || [],
5911
+ toolCalls: typedHistoryEntry.toolCalls,
5912
+ toolResponses: typedHistoryEntry.toolResponses
5913
+ });
5914
+ if (traceSummary) {
5915
+ assistantContextContent = assistantBaseContent ? `${assistantBaseContent}
5916
+
5917
+ ${traceSummary}` : traceSummary;
5918
+ }
5919
+ }
5920
+ newMessages.push({
5921
+ role: "user",
5922
+ content: [
5923
+ {
5924
+ type: "text",
5925
+ text: promptForHistory
5926
+ }
5927
+ ]
5928
+ });
5929
+ newMessages.push({
5930
+ role: "assistant",
5931
+ content: [
5932
+ {
5933
+ type: "text",
5934
+ text: assistantContextContent
5935
+ }
5936
+ ]
5937
+ });
5938
+ });
5939
+ if (newMessages.length === 0 && userPrompt.trim().length > 0) {
5940
+ newMessages.push({
5941
+ role: "user",
5942
+ content: [
5943
+ {
5944
+ type: "text",
5945
+ text: userPrompt
5946
+ }
5947
+ ]
5948
+ });
5949
+ if (assistantSeedText.trim().length > 0) {
5950
+ newMessages.push({
5951
+ role: "assistant",
5952
+ content: [
5953
+ {
5954
+ type: "text",
5955
+ text: assistantSeedText
5956
+ }
5957
+ ]
5958
+ });
5959
+ }
5960
+ }
5961
+ const parsedToolCalls = yield Promise.all(
5962
+ requestsToProcess.map((req, index) => __async(void 0, null, function* () {
5963
+ var _a3, _b2, _c2, _d, _e, _f;
5964
+ let parsedToolCall = null;
5965
+ try {
5966
+ parsedToolCall = JSON.parse(req.match);
5967
+ } catch (error2) {
5968
+ console.error("[AIChatPanel] Failed to parse tool call payload:", error2);
5969
+ }
5970
+ const toolName = req.groups[1] || req.toolName || (typeof (parsedToolCall == null ? void 0 : parsedToolCall.name) === "string" ? parsedToolCall.name : "") || (typeof ((_a3 = parsedToolCall == null ? void 0 : parsedToolCall.function) == null ? void 0 : _a3.name) === "string" ? parsedToolCall.function.name : "");
5971
+ if (!toolName) return null;
5972
+ const rawCallId = req.callId || req.groups[0] || (parsedToolCall == null ? void 0 : parsedToolCall.id) || (parsedToolCall == null ? void 0 : parsedToolCall.tool_call_id) || `${toolName}-${index + 1}`;
5973
+ const callId = typeof rawCallId === "string" && rawCallId.trim().length > 0 && rawCallId !== "functionCall" ? rawCallId : `${toolName}-${index + 1}`;
5974
+ let args = {};
5975
+ const rawArgs = (_f = (_e = (_c2 = (_b2 = req.groups[2]) != null ? _b2 : parsedToolCall == null ? void 0 : parsedToolCall.input) != null ? _c2 : parsedToolCall == null ? void 0 : parsedToolCall.args) != null ? _e : (_d = parsedToolCall == null ? void 0 : parsedToolCall.function) == null ? void 0 : _d.arguments) != null ? _f : "{}";
5976
+ const parsedArgs = parseToolArguments(rawArgs);
5977
+ if (!parsedArgs) {
5978
+ console.error("[AIChatPanel] Failed to parse tool arguments", {
5979
+ toolName,
5980
+ callId,
5981
+ rawArgsPreview: typeof rawArgs === "string" ? rawArgs.slice(0, 500) : JSON.stringify(rawArgs).slice(0, 500)
5982
+ });
5983
+ return null;
5984
+ }
5985
+ args = parsedArgs;
5986
+ const serviceTag = typeof req.serviceTag === "string" && req.serviceTag || typeof req.groups[3] === "string" && req.groups[3] || typeof (parsedToolCall == null ? void 0 : parsedToolCall.service) === "string" && parsedToolCall.service || "";
5987
+ const callSignature = getToolCallSignature(toolName, callId);
5988
+ if (!callSignature) return null;
5989
+ return {
5990
+ req,
5991
+ toolName,
5992
+ callId,
5993
+ args,
5994
+ serviceTag,
5995
+ callSignature
5996
+ };
5997
+ }))
5998
+ );
5999
+ const toolCallBatch = parsedToolCalls.filter(Boolean);
6000
+ const seenCallSignatures = /* @__PURE__ */ new Set();
6001
+ const callsToRun = [];
6002
+ toolCallBatch.forEach((toolCall) => {
6003
+ if (seenCallSignatures.has(toolCall.callSignature)) return;
6004
+ seenCallSignatures.add(toolCall.callSignature);
6005
+ if (handledToolCallSignaturesRef.current.has(toolCall.callSignature)) return;
6006
+ if (inFlightToolCallSignaturesRef.current.has(toolCall.callSignature)) return;
6007
+ callsToRun.push(toolCall);
6008
+ });
6009
+ if (callsToRun.length === 0) {
6010
+ setPendingToolRequests(
6011
+ (prev) => prev.filter((request) => {
6012
+ const signature = getToolCallSignature(request.toolName, request.callId);
6013
+ if (!signature) return true;
6014
+ return !seenCallSignatures.has(signature);
6015
+ })
6016
+ );
6017
+ setActiveToolCalls([]);
6018
+ setIsLoading(false);
6019
+ return;
6020
+ }
6021
+ const callsToRunSignatures = new Set(callsToRun.map((toolCall) => toolCall.callSignature));
6022
+ setPendingToolRequests(
6023
+ (prev) => prev.filter((request) => {
6024
+ const signature = getToolCallSignature(request.toolName, request.callId);
6025
+ return !signature || !callsToRunSignatures.has(signature);
6026
+ })
6027
+ );
6028
+ callsToRun.forEach((toolCall) => {
6029
+ inFlightToolCallSignaturesRef.current.add(toolCall.callSignature);
6030
+ });
6031
+ setActiveToolCalls(
6032
+ callsToRun.map((toolCall) => ({
6033
+ toolName: toolCall.toolName,
6034
+ callId: toolCall.callId
6035
+ }))
6036
+ );
6037
+ const finalToolCalls = callsToRun.map((toolCall) => ({
6038
+ id: toolCall.callId,
6039
+ type: "tool_use",
6040
+ name: toolCall.toolName,
6041
+ input: toolCall.args,
6042
+ service: toolCall.serviceTag
6043
+ }));
6044
+ let finalToolResponses = [];
6045
+ try {
6046
+ const toolResponses = yield Promise.all(
6047
+ callsToRun.map((toolCall) => __async(void 0, null, function* () {
6048
+ var _a3, _b2, _c2, _d, _e, _f, _g;
6049
+ const mcpTool = toolList.find((tool) => tool.name === toolCall.toolName) || null;
6050
+ const localExecutor = localToolExecutors && typeof localToolExecutors[toolCall.toolName] === "function" ? localToolExecutors[toolCall.toolName] : null;
6051
+ if (localExecutor) {
6052
+ try {
6053
+ const localResult = yield localExecutor(toolCall.args, {
6054
+ toolName: toolCall.toolName,
6055
+ callId: toolCall.callId,
6056
+ serviceTag: toolCall.serviceTag,
6057
+ mcpTool
6058
+ });
6059
+ if (localResult && typeof localResult === "object" && !Array.isArray(localResult)) {
6060
+ const objectResult = localResult;
6061
+ const isError = objectResult.isError === true || objectResult.error === true;
6062
+ const maybeResult = Object.prototype.hasOwnProperty.call(objectResult, "result") ? objectResult.result : localResult;
6063
+ const textResult = typeof maybeResult === "string" ? maybeResult : JSON.stringify(maybeResult != null ? maybeResult : {});
6064
+ return {
6065
+ tool_call_id: toolCall.callId,
6066
+ tool_name: toolCall.toolName,
6067
+ result: textResult || "",
6068
+ isError
6069
+ };
6070
+ }
6071
+ return {
6072
+ tool_call_id: toolCall.callId,
6073
+ tool_name: toolCall.toolName,
6074
+ result: typeof localResult === "string" ? localResult : JSON.stringify(localResult != null ? localResult : {}),
6075
+ isError: false
6076
+ };
6077
+ } catch (error2) {
6078
+ return {
6079
+ tool_call_id: toolCall.callId,
6080
+ tool_name: toolCall.toolName,
6081
+ result: error2 instanceof Error ? error2.message : `Unhandled error calling ${toolCall.toolName}`,
6082
+ isError: true
6083
+ };
6084
+ }
6085
+ }
6086
+ if (!mcpTool) {
6087
+ console.error(`[AIChatPanel] Tool ${toolCall.toolName} not found in tool list`);
6088
+ return {
6089
+ tool_call_id: toolCall.callId,
6090
+ tool_name: toolCall.toolName,
6091
+ result: `Tool ${toolCall.toolName} not found in current tool list.`,
6092
+ isError: true
6093
+ };
6094
+ }
6095
+ const toolUrl = typeof mcpTool.url === "string" ? mcpTool.url : "";
6096
+ if (!toolUrl) {
6097
+ return {
6098
+ tool_call_id: toolCall.callId,
6099
+ tool_name: toolCall.toolName,
6100
+ result: `Tool ${toolCall.toolName} is missing url metadata.`,
6101
+ isError: true
6102
+ };
6103
+ }
6104
+ const body = {
6105
+ tool: toolCall.toolName,
6106
+ args: toolCall.args
6107
+ };
6108
+ try {
6109
+ const result = yield fetch(
6110
+ `${publicAPIUrl}/tools/${encodeURIComponent(toolUrl)}`,
6111
+ {
6112
+ method: "POST",
6113
+ headers: __spreadValues({
6114
+ "Content-Type": "application/json"
6115
+ }, yield buildMcpRequestHeaders({
6116
+ phase: "call",
6117
+ mcpServer: mcpTool,
6118
+ toolName: toolCall.toolName,
6119
+ toolArgs: toolCall.args
6120
+ })),
6121
+ body: JSON.stringify(body)
6122
+ }
6123
+ );
6124
+ if (!result.ok) {
6125
+ const errorBody = yield result.text();
6126
+ console.error(
6127
+ `[AIChatPanel] Tool call failed ${toolCall.toolName}: ${result.status} ${result.statusText} ${errorBody}`
6128
+ );
6129
+ return {
6130
+ tool_call_id: toolCall.callId,
6131
+ tool_name: toolCall.toolName,
6132
+ result: `HTTP ${result.status} ${result.statusText}: ${errorBody || "Tool call failed"}`,
6133
+ isError: true
6134
+ };
6135
+ }
6136
+ let resultData = null;
6137
+ try {
6138
+ resultData = yield result.json();
6139
+ } catch (error2) {
6140
+ console.error("[AIChatPanel] Failed parsing tool call JSON response:", error2);
6141
+ return {
6142
+ tool_call_id: toolCall.callId,
6143
+ tool_name: toolCall.toolName,
6144
+ result: "Tool returned a non-JSON response.",
6145
+ isError: true
6146
+ };
6147
+ }
6148
+ const textResult = (_c2 = (_b2 = (_a3 = resultData == null ? void 0 : resultData.content) == null ? void 0 : _a3[0]) == null ? void 0 : _b2.text) != null ? _c2 : (resultData == null ? void 0 : resultData.result) ? JSON.stringify(resultData.result) : JSON.stringify(resultData);
6149
+ const inferredError = (resultData == null ? void 0 : resultData.isError) === true || (resultData == null ? void 0 : resultData.error) === true || typeof (resultData == null ? void 0 : resultData.error) === "string" || (resultData == null ? void 0 : resultData.status) === "error" || ((_d = resultData == null ? void 0 : resultData.result) == null ? void 0 : _d.isError) === true || ((_e = resultData == null ? void 0 : resultData.result) == null ? void 0 : _e.error) === true || typeof ((_f = resultData == null ? void 0 : resultData.result) == null ? void 0 : _f.error) === "string";
6150
+ const normalizedResultText = typeof textResult === "string" && textResult.trim().length > 0 ? textResult : inferredError && typeof (resultData == null ? void 0 : resultData.error) === "string" ? resultData.error : inferredError && typeof ((_g = resultData == null ? void 0 : resultData.result) == null ? void 0 : _g.error) === "string" ? resultData.result.error : "";
6151
+ return {
6152
+ tool_call_id: toolCall.callId,
6153
+ tool_name: toolCall.toolName,
6154
+ result: normalizedResultText,
6155
+ isError: inferredError
6156
+ };
6157
+ } catch (error2) {
6158
+ console.error(`[AIChatPanel] Error calling tool ${toolCall.toolName}:`, error2);
6159
+ return {
6160
+ tool_call_id: toolCall.callId,
6161
+ tool_name: toolCall.toolName,
6162
+ result: error2 instanceof Error ? error2.message : `Unhandled error calling ${toolCall.toolName}`,
6163
+ isError: true
6164
+ };
6165
+ }
6166
+ }))
6167
+ );
6168
+ finalToolResponses = toolResponses.filter(Boolean);
6169
+ } finally {
6170
+ callsToRun.forEach((toolCall) => {
6171
+ inFlightToolCallSignaturesRef.current.delete(toolCall.callSignature);
6172
+ handledToolCallSignaturesRef.current.add(toolCall.callSignature);
6173
+ });
6174
+ }
6175
+ setActiveToolCalls([]);
6176
+ const currentLastKey = lastKeyRef.current;
6177
+ if (currentLastKey) {
6178
+ setHistory((prev) => {
6179
+ const existingEntry = prev[currentLastKey] || { content: "", callId: "" };
6180
+ return __spreadProps(__spreadValues({}, prev), {
6181
+ [currentLastKey]: __spreadProps(__spreadValues({}, existingEntry), {
6182
+ toolCalls: [...existingEntry.toolCalls || [], ...finalToolCalls],
6183
+ toolResponses: [...existingEntry.toolResponses || [], ...finalToolResponses]
6184
+ })
6185
+ });
6186
+ });
6187
+ }
6188
+ const toReplayText = (value) => {
6189
+ const raw = typeof value === "string" ? value : (() => {
6190
+ try {
6191
+ return JSON.stringify(value);
6192
+ } catch (_error) {
6193
+ return String(value != null ? value : "");
6194
+ }
6195
+ })();
6196
+ return String(raw != null ? raw : "");
6197
+ };
6198
+ const replayEntryKey = lastKeyRef.current || "";
6199
+ const previousReplayEntries = replayEntryKey ? toolReplaySummariesByKeyRef.current[replayEntryKey] || [] : [];
6200
+ const currentReplayEntries = callsToRun.map((toolCall, index) => {
6201
+ var _a3;
6202
+ const matchedResponse = finalToolResponses.find((response2) => (response2 == null ? void 0 : response2.tool_call_id) === toolCall.callId) || finalToolResponses[index];
6203
+ return {
6204
+ toolName: toolCall.toolName,
6205
+ callId: toolCall.callId,
6206
+ status: (matchedResponse == null ? void 0 : matchedResponse.isError) ? "error" : "ok",
6207
+ argsText: toReplayText(toolCall.args),
6208
+ resultText: toReplayText((_a3 = matchedResponse == null ? void 0 : matchedResponse.result) != null ? _a3 : "No result returned")
6209
+ };
6210
+ });
6211
+ const replayEntries = [...previousReplayEntries, ...currentReplayEntries];
6212
+ if (replayEntryKey) {
6213
+ toolReplaySummariesByKeyRef.current[replayEntryKey] = replayEntries;
6214
+ }
6215
+ const replayLines = replayEntries.map(
6216
+ (entry) => [
6217
+ `Tool: ${entry.toolName}`,
6218
+ `Call ID: ${entry.callId}`,
6219
+ `Status: ${entry.status}`,
6220
+ `Args: ${entry.argsText}`,
6221
+ `Result: ${entry.resultText}`
6222
+ ].join("\n")
6223
+ );
6224
+ const originalRequest = typeof lastPromptRef.current === "string" && lastPromptRef.current.trim() || typeof userPrompt === "string" && userPrompt.trim() || "";
6225
+ const continuationPromptText = [
6226
+ originalRequest ? `Original request: ${originalRequest}` : "",
6227
+ "Tool execution summary for the previous request:",
6228
+ ...replayLines,
6229
+ "Continue the same assistant response from exactly where you paused using these tool results.",
6230
+ "Treat successful mutating tool results above as already completed actions. Do not repeat those same mutating tool calls unless the user explicitly asks to retry.",
6231
+ "If you include meta tags, use only <thinking>, <reasoning>, <searching> for internal process.",
6232
+ "Put the final user-facing answer outside all meta tags."
6233
+ ].filter(Boolean).join("\n\n");
6234
+ if (continuationPromptText.length > MAX_TOOL_REPLAY_PAYLOAD_CHARS) {
6235
+ setActiveToolCalls([]);
6236
+ setIsLoading(false);
6237
+ setError({
6238
+ message: "Tool result payload is too large to continue safely in a single turn. Narrow the query or fetch steps in chunks.",
6239
+ code: "TOOL_REPLAY_TOO_LARGE"
6240
+ });
6241
+ return;
6242
+ }
6243
+ newMessages.push({
6244
+ role: "user",
6245
+ content: [
6246
+ {
6247
+ type: "text",
6248
+ text: continuationPromptText
6249
+ }
6250
+ ]
6251
+ });
6252
+ if (toolContinuationCountRef.current >= MAX_TOOL_CONTINUATIONS_PER_TURN) {
6253
+ setActiveToolCalls([]);
6254
+ setIsLoading(false);
6255
+ setError({
6256
+ message: "Tool continuation limit reached for this response. Please refine the prompt and retry.",
6257
+ code: "TOOL_CONTINUATION_LIMIT"
6258
+ });
6259
+ return;
6260
+ }
6261
+ toolContinuationCountRef.current += 1;
6262
+ if (!streamIdleRef.current) {
6263
+ yield waitForStreamIdle(1e4);
6264
+ }
6265
+ if (!streamIdleRef.current) {
6266
+ suppressAbortHistoryUpdateRef.current = true;
6267
+ try {
6268
+ stop(lastController);
6269
+ yield waitForStreamIdle(3e3);
6270
+ } finally {
6271
+ suppressAbortHistoryUpdateRef.current = false;
6272
+ }
6273
+ }
6274
+ if (!streamIdleRef.current) {
6275
+ setActiveToolCalls([]);
6276
+ setIsLoading(false);
6277
+ setError({
6278
+ message: "Timed out waiting for the previous stream to settle before tool continuation.",
6279
+ code: "TOOL_CONTINUATION_WAIT_TIMEOUT"
6280
+ });
6281
+ return;
6282
+ }
6283
+ const newController = new AbortController();
6284
+ setLastController(newController);
6285
+ const continuationKey = lastKeyRef.current;
6286
+ if (continuationKey) {
6287
+ const continuationBase = ((_c = latestHistoryRef.current[continuationKey]) == null ? void 0 : _c.content) || "";
6288
+ activeStreamAppendBaseRef.current = continuationBase.trim().length > 0 ? { key: continuationKey, base: continuationBase } : null;
6289
+ } else {
6290
+ activeStreamAppendBaseRef.current = null;
6291
+ }
6292
+ send(
6293
+ "",
6294
+ newMessages,
6295
+ [
6296
+ ...dataWithExtras(),
6297
+ {
6298
+ key: "--messages",
6299
+ data: newMessages.length.toString()
6300
+ }
6301
+ ],
6302
+ true,
6303
+ true,
6304
+ service,
6305
+ currentConversation,
6306
+ newController,
6307
+ void 0,
6308
+ (errorMsg) => {
6309
+ setActiveToolCalls([]);
6310
+ setIsLoading(false);
6311
+ setError({
6312
+ message: errorMsg,
6313
+ code: "TOOL_ERROR"
6314
+ });
6315
+ }
6316
+ );
6317
+ } finally {
6318
+ toolRequestProcessingRef.current = false;
6319
+ const queued = queuedToolRequestsRef.current;
6320
+ if (queued && queued.length > 0) {
6321
+ queuedToolRequestsRef.current = null;
6322
+ queueMicrotask(() => {
6323
+ void processGivenToolRequests(queued);
6324
+ });
6325
+ }
6326
+ }
6327
+ }),
6328
+ [
6329
+ toolList,
6330
+ localToolExecutors,
6331
+ publicAPIUrl,
6332
+ buildMcpRequestHeaders,
6333
+ dataWithExtras,
6334
+ send,
6335
+ service,
6336
+ currentConversation,
6337
+ setActiveToolCalls,
6338
+ getToolCallSignature,
6339
+ traceContextMode,
6340
+ idle,
6341
+ stop,
6342
+ lastController,
6343
+ waitForStreamIdle
6344
+ ]
6345
+ );
6346
+ const handleToolApproval = (0, import_react14.useCallback)(
6347
+ (toolName, scope) => {
6348
+ const normalizedToolName = normalizeToolName(toolName);
6349
+ if (!normalizedToolName) return;
6350
+ if (scope === "session" || scope === "always") {
6351
+ setSessionApprovedTools((prev) => Array.from(/* @__PURE__ */ new Set([...prev, normalizedToolName])));
6352
+ }
6353
+ if (scope === "always") {
6354
+ setAlwaysApprovedTools((prev) => Array.from(/* @__PURE__ */ new Set([...prev, normalizedToolName])));
6355
+ }
6356
+ const requestsToRun = (pendingToolRequestsRef.current || []).filter(
6357
+ (r) => normalizeToolName(r.toolName) === normalizedToolName
6358
+ );
6359
+ void processGivenToolRequests(requestsToRun);
6360
+ setPendingToolRequests(
6361
+ (prev) => prev.filter((r) => normalizeToolName(r.toolName) !== normalizedToolName)
6362
+ );
6363
+ },
6364
+ [processGivenToolRequests, normalizeToolName]
6365
+ );
6366
+ (0, import_react14.useEffect)(() => {
6367
+ if (pendingToolRequests.length === 0) return;
6368
+ const configuredAutoApproveTools = Array.isArray(autoApproveTools) ? new Set(
6369
+ autoApproveTools.map((toolName) => normalizeToolName(String(toolName))).filter(Boolean)
6370
+ ) : null;
6371
+ const toAuto = pendingToolRequests.filter(
6372
+ (r) => {
6373
+ const normalized = normalizeToolName(r.toolName);
6374
+ if (!normalized) return false;
6375
+ if (autoApproveTools === true) return true;
6376
+ if (configuredAutoApproveTools == null ? void 0 : configuredAutoApproveTools.has(normalized)) return true;
6377
+ return sessionApprovedTools.includes(normalized) || alwaysApprovedTools.includes(normalized);
6378
+ }
6379
+ );
6380
+ if (toAuto.length > 0) {
6381
+ void processGivenToolRequests(toAuto);
6382
+ setPendingToolRequests(
6383
+ (prev) => prev.filter(
6384
+ (r) => {
6385
+ const normalized = normalizeToolName(r.toolName);
6386
+ if (!normalized) return true;
6387
+ if (autoApproveTools === true) return false;
6388
+ if (configuredAutoApproveTools == null ? void 0 : configuredAutoApproveTools.has(normalized)) return false;
6389
+ return !sessionApprovedTools.includes(normalized) && !alwaysApprovedTools.includes(normalized);
6390
+ }
6391
+ )
6392
+ );
6393
+ }
6394
+ }, [
6395
+ autoApproveTools,
6396
+ pendingToolRequests,
6397
+ sessionApprovedTools,
6398
+ alwaysApprovedTools,
6399
+ processGivenToolRequests,
6400
+ normalizeToolName
6401
+ ]);
4583
6402
  const cleanContentForDisplay = (0, import_react14.useCallback)((content) => {
4584
6403
  let cleaned = content.replace(/\*\*(.*?)\*\*/g, "$1").replace(/\*(.*?)\*/g, "$1").replace(/\n+/g, " ").replace(/\s+/g, " ").trim();
4585
6404
  if (cleaned.length > 100) {
@@ -4588,7 +6407,6 @@ var AIChatPanel = ({
4588
6407
  return cleaned || "Thinking";
4589
6408
  }, []);
4590
6409
  const processThinkingTags = (0, import_react14.useCallback)((text) => {
4591
- var _a2, _b, _c;
4592
6410
  if (!text) {
4593
6411
  return {
4594
6412
  cleanedText: "",
@@ -4598,30 +6416,28 @@ var AIChatPanel = ({
4598
6416
  };
4599
6417
  }
4600
6418
  const processedText = text.replace(/\u200B/g, "");
4601
- const allMatches = [];
4602
- const thinkingRegex = /<thinking>([\s\S]*?)<\/thinking>/gi;
4603
- let match;
4604
- while ((match = thinkingRegex.exec(processedText)) !== null) {
4605
- const content = (_a2 = match[1]) == null ? void 0 : _a2.trim();
4606
- if (content) {
4607
- allMatches.push({ content, index: match.index, type: "thinking" });
4608
- }
4609
- }
4610
- const reasoningRegex = /<reasoning>([\s\S]*?)<\/reasoning>/gi;
4611
- while ((match = reasoningRegex.exec(processedText)) !== null) {
4612
- const content = (_b = match[1]) == null ? void 0 : _b.trim();
4613
- if (content) {
4614
- allMatches.push({ content, index: match.index, type: "reasoning" });
4615
- }
4616
- }
4617
- const searchingRegex = /<searching>([\s\S]*?)<\/searching>/gi;
4618
- while ((match = searchingRegex.exec(processedText)) !== null) {
4619
- const content = (_c = match[1]) == null ? void 0 : _c.trim();
4620
- if (content) {
4621
- allMatches.push({ content, index: match.index, type: "searching" });
6419
+ const completedBlocks = [];
6420
+ const textWithCompleteMarkers = processedText.replace(
6421
+ /<(thinking|reasoning|searching)>([\s\S]*?)<\/\1>/gi,
6422
+ (_fullMatch, rawType, rawContent, offset) => {
6423
+ const normalizedType = String(rawType || "").trim().toLowerCase();
6424
+ const type = normalizedType === "reasoning" ? "reasoning" : normalizedType === "searching" ? "searching" : "thinking";
6425
+ const content = String(rawContent || "").trim();
6426
+ if (!content) return "\n\n";
6427
+ const signature = getThinkingBlockSignature(type, content);
6428
+ completedBlocks.push({
6429
+ type,
6430
+ content,
6431
+ index: Number(offset || 0),
6432
+ signature
6433
+ });
6434
+ return `
6435
+
6436
+ ${buildThinkingBlockMarker(type, signature)}
6437
+
6438
+ `;
4622
6439
  }
4623
- }
4624
- const completedBlocks = allMatches.sort((a, b) => a.index - b.index);
6440
+ );
4625
6441
  let activeBlock = null;
4626
6442
  const tagTypes = ["thinking", "reasoning", "searching"];
4627
6443
  let latestIncompletePos = -1;
@@ -4651,7 +6467,7 @@ var AIChatPanel = ({
4651
6467
  }
4652
6468
  }
4653
6469
  }
4654
- let cleanedText = processedText.replace(/<thinking>[\s\S]*?<\/thinking>/gi, "").replace(/<reasoning>[\s\S]*?<\/reasoning>/gi, "").replace(/<searching>[\s\S]*?<\/searching>/gi, "").replace(/<think(?:i(?:n(?:g)?)?)?$/i, "").replace(/<reas(?:o(?:n(?:i(?:n(?:g)?)?)?)?)?$/i, "").replace(/<sear(?:c(?:h(?:i(?:n(?:g)?)?)?)?)?$/i, "").replace(/<thinking>[\s\S]*$/i, "").replace(/<reasoning>[\s\S]*$/i, "").replace(/<searching>[\s\S]*$/i, "").trim();
6470
+ let cleanedText = textWithCompleteMarkers.replace(/<think(?:i(?:n(?:g)?)?)?$/i, "").replace(/<reas(?:o(?:n(?:i(?:n(?:g)?)?)?)?)?$/i, "").replace(/<sear(?:c(?:h(?:i(?:n(?:g)?)?)?)?)?$/i, "").replace(/<thinking>[\s\S]*$/i, "").replace(/<reasoning>[\s\S]*$/i, "").replace(/<searching>[\s\S]*$/i, "").trim();
4655
6471
  let lastThinkingContent = "Thinking";
4656
6472
  if (completedBlocks.length > 0) {
4657
6473
  const lastBlock = completedBlocks[completedBlocks.length - 1];
@@ -4663,15 +6479,67 @@ var AIChatPanel = ({
4663
6479
  }
4664
6480
  return { cleanedText, completedBlocks, activeBlock, lastThinkingContent };
4665
6481
  }, [cleanContentForDisplay]);
6482
+ const mergeThinkingBlocks = (0, import_react14.useCallback)(
6483
+ (existing, incoming) => {
6484
+ if (incoming.length === 0) return existing;
6485
+ if (existing.length === 0) return incoming;
6486
+ const incomingSupersetPrefix = incoming.length >= existing.length && existing.every((block, index) => {
6487
+ const next = incoming[index];
6488
+ return !!next && next.type === block.type && next.content === block.content;
6489
+ });
6490
+ if (incomingSupersetPrefix) {
6491
+ return incoming;
6492
+ }
6493
+ const merged = [...existing];
6494
+ const seen = new Set(existing.map((block) => block.signature || `${block.type}::${block.content}`));
6495
+ for (const block of incoming) {
6496
+ const signature = block.signature || `${block.type}::${block.content}`;
6497
+ if (seen.has(signature)) continue;
6498
+ seen.add(signature);
6499
+ merged.push(block);
6500
+ }
6501
+ return merged;
6502
+ },
6503
+ []
6504
+ );
6505
+ const getThinkingBlockCollapseKey = (0, import_react14.useCallback)((entryKey, blockKey) => {
6506
+ return `${entryKey}::${blockKey}`;
6507
+ }, []);
6508
+ const getThinkingBlockRenderKey = (0, import_react14.useCallback)((block, fallbackIndex) => {
6509
+ return String((block == null ? void 0 : block.signature) || "").trim() || `block-${fallbackIndex}`;
6510
+ }, []);
6511
+ const AGENT_SUGGESTION_ACTION = {
6512
+ pattern: "\\[SUGGEST_AGENT:([^|\\]]+)\\|([^|\\]]+)\\|([^\\]]+)\\]",
6513
+ markdown: '<agent-suggestion data-agent-id="$1" data-agent-name="$2" data-reason="$3"></agent-suggestion>'
6514
+ };
6515
+ const extractToolRequests = (0, import_react14.useCallback)((rawResponse) => {
6516
+ return extractToolRequestMatchesFromText(rawResponse);
6517
+ }, []);
6518
+ const formatToolRequestsForDisplay = (0, import_react14.useCallback)((rawResponse) => {
6519
+ if (!rawResponse) return rawResponse;
6520
+ const requests = extractToolRequestMatchesFromText(rawResponse);
6521
+ if (requests.length === 0) return stripStandaloneRawToolJsonLines(rawResponse);
6522
+ const output = [];
6523
+ let cursor = 0;
6524
+ requests.forEach((request) => {
6525
+ const start = Math.max(0, Math.min(rawResponse.length, request.start));
6526
+ const end = Math.max(start, Math.min(rawResponse.length, request.end));
6527
+ output.push(rawResponse.slice(cursor, start));
6528
+ output.push(`
6529
+
6530
+ ${buildInlineToolMarker(request.toolName, request.callId)}
6531
+
6532
+ `);
6533
+ cursor = end;
6534
+ });
6535
+ output.push(rawResponse.slice(cursor));
6536
+ return stripStandaloneRawToolJsonLines(output.join(""));
6537
+ }, []);
4666
6538
  const activeThinkingBlock = (0, import_react14.useMemo)(() => {
4667
6539
  if (!response || justReset) return null;
4668
6540
  const { activeBlock } = processThinkingTags(response);
4669
6541
  return activeBlock;
4670
6542
  }, [response, justReset, processThinkingTags]);
4671
- const AGENT_SUGGESTION_ACTION = {
4672
- pattern: "\\[SUGGEST_AGENT:([^|\\]]+)\\|([^|\\]]+)\\|([^\\]]+)\\]",
4673
- markdown: '<agent-suggestion data-agent-id="$1" data-agent-name="$2" data-reason="$3"></agent-suggestion>'
4674
- };
4675
6543
  const processActions = (0, import_react14.useCallback)((content) => {
4676
6544
  const allActions = [AGENT_SUGGESTION_ACTION, ...actions || []];
4677
6545
  let processed = content;
@@ -4705,6 +6573,38 @@ var AIChatPanel = ({
4705
6573
  }
4706
6574
  return displayPrompt;
4707
6575
  }, [hideRagContextInPrompt]);
6576
+ const normalizeHistoryPromptForContext = (0, import_react14.useCallback)((historyPrompt) => {
6577
+ let promptForHistory = String(historyPrompt || "");
6578
+ const isoTimestampRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z:/;
6579
+ if (isoTimestampRegex.test(promptForHistory)) {
6580
+ const colonIndex = promptForHistory.indexOf(":", 19);
6581
+ promptForHistory = promptForHistory.substring(colonIndex + 1);
6582
+ } else if (/^\d+:/.test(promptForHistory)) {
6583
+ const colonIndex = promptForHistory.indexOf(":");
6584
+ promptForHistory = promptForHistory.substring(colonIndex + 1);
6585
+ }
6586
+ return promptForHistory;
6587
+ }, []);
6588
+ const buildAssistantContextContent = (0, import_react14.useCallback)(
6589
+ (historyPrompt, historyEntry) => {
6590
+ const assistantBaseContent = typeof (historyEntry == null ? void 0 : historyEntry.content) === "string" ? historyEntry.content : "";
6591
+ if (traceContextMode !== "full") {
6592
+ return assistantBaseContent;
6593
+ }
6594
+ const traceSummary = buildCompactTraceSummary({
6595
+ reasoningBlocks: thinkingBlocksByKeyRef.current[historyPrompt] || [],
6596
+ toolCalls: historyEntry == null ? void 0 : historyEntry.toolCalls,
6597
+ toolResponses: historyEntry == null ? void 0 : historyEntry.toolResponses
6598
+ });
6599
+ if (!traceSummary) {
6600
+ return assistantBaseContent;
6601
+ }
6602
+ return assistantBaseContent ? `${assistantBaseContent}
6603
+
6604
+ ${traceSummary}` : traceSummary;
6605
+ },
6606
+ [traceContextMode]
6607
+ );
4708
6608
  const interactionClicked = (0, import_react14.useCallback)((callId, action, emailaddress = "", comment = "") => __async(void 0, null, function* () {
4709
6609
  var _a2, _b;
4710
6610
  console.log(`[AIChatPanel] Interaction: ${action} for callId: ${callId}`);
@@ -4787,9 +6687,24 @@ var AIChatPanel = ({
4787
6687
  lastScrollTimeRef.current = now;
4788
6688
  }, []);
4789
6689
  const continueChat = (0, import_react14.useCallback)((promptText) => {
6690
+ handledToolCallSignaturesRef.current = /* @__PURE__ */ new Set();
6691
+ inFlightToolCallSignaturesRef.current = /* @__PURE__ */ new Set();
6692
+ toolContinuationCountRef.current = 0;
6693
+ activeStreamAppendBaseRef.current = null;
6694
+ toolReplaySummariesByKeyRef.current = {};
6695
+ setPendingToolRequests([]);
6696
+ setActiveToolCalls([]);
6697
+ setCollapsedBlocks((prev) => {
6698
+ const next = new Set(prev);
6699
+ Object.entries(thinkingBlocksByKeyRef.current).forEach(([entryKey, blocks]) => {
6700
+ blocks.forEach((block, index) => {
6701
+ next.add(getThinkingBlockCollapseKey(entryKey, getThinkingBlockRenderKey(block, index)));
6702
+ });
6703
+ });
6704
+ return next;
6705
+ });
4790
6706
  setThinkingBlocks([]);
4791
6707
  setCurrentThinkingIndex(0);
4792
- setCollapsedBlocks(/* @__PURE__ */ new Set());
4793
6708
  hasAutoCollapsedRef.current = false;
4794
6709
  prevBlockCountRef.current = 0;
4795
6710
  setError(null);
@@ -4818,6 +6733,7 @@ var AIChatPanel = ({
4818
6733
  setHistory((prevHistory) => __spreadProps(__spreadValues({}, prevHistory), {
4819
6734
  [promptKey]: { content: "", callId: "" }
4820
6735
  }));
6736
+ toolReplaySummariesByKeyRef.current[promptKey] = [];
4821
6737
  setLastPrompt(promptToSend.trim());
4822
6738
  setLastKey(promptKey);
4823
6739
  setTimeout(() => {
@@ -4826,20 +6742,14 @@ var AIChatPanel = ({
4826
6742
  console.log("AIChatPanel.continueChat - about to call ensureConversation");
4827
6743
  ensureConversation().then((convId) => {
4828
6744
  console.log("AIChatPanel.continueChat - ensureConversation resolved with:", convId);
6745
+ const historyForCall = latestHistoryRef.current || {};
4829
6746
  const messagesAndHistory = [];
4830
- Object.entries(history).forEach(([historyPrompt, historyEntry]) => {
6747
+ Object.entries(historyForCall).forEach(([historyPrompt, historyEntry]) => {
4831
6748
  if (historyPrompt === promptKey) return;
4832
- let promptForHistory = historyPrompt;
4833
- const isoTimestampRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z:/;
4834
- if (isoTimestampRegex.test(historyPrompt)) {
4835
- const colonIndex = historyPrompt.indexOf(":", 19);
4836
- promptForHistory = historyPrompt.substring(colonIndex + 1);
4837
- } else if (/^\d+:/.test(historyPrompt)) {
4838
- const colonIndex = historyPrompt.indexOf(":");
4839
- promptForHistory = historyPrompt.substring(colonIndex + 1);
4840
- }
6749
+ const promptForHistory = normalizeHistoryPromptForContext(historyPrompt);
6750
+ const assistantContextContent = buildAssistantContextContent(historyPrompt, historyEntry);
4841
6751
  messagesAndHistory.push({ role: "user", content: promptForHistory });
4842
- messagesAndHistory.push({ role: "assistant", content: historyEntry.content });
6752
+ messagesAndHistory.push({ role: "assistant", content: assistantContextContent });
4843
6753
  });
4844
6754
  let fullPromptToSend = promptToSend.trim();
4845
6755
  if (messagesAndHistory.length === 0 && promptTemplate) {
@@ -4847,6 +6757,19 @@ var AIChatPanel = ({
4847
6757
  }
4848
6758
  const newController = new AbortController();
4849
6759
  setLastController(newController);
6760
+ if (onBeforeSend) {
6761
+ void Promise.resolve(
6762
+ onBeforeSend({
6763
+ prompt: promptToSend.trim(),
6764
+ conversationId: convId || null,
6765
+ agentId: agent,
6766
+ service,
6767
+ messages: messagesAndHistory
6768
+ })
6769
+ ).catch((error2) => {
6770
+ console.warn("[AIChatPanel] onBeforeSend callback failed:", error2);
6771
+ });
6772
+ }
4850
6773
  send(
4851
6774
  fullPromptToSend,
4852
6775
  messagesAndHistory,
@@ -4869,14 +6792,21 @@ var AIChatPanel = ({
4869
6792
  console.log("[AIChatPanel] Error callback triggered:", errorMsg);
4870
6793
  const isAbortError = errorMsg.toLowerCase().includes("abort") || errorMsg.toLowerCase().includes("canceled") || errorMsg.toLowerCase().includes("cancelled");
4871
6794
  if (isAbortError) {
6795
+ if (suppressAbortHistoryUpdateRef.current) {
6796
+ setIsLoading(false);
6797
+ return;
6798
+ }
4872
6799
  console.log("[AIChatPanel] Request was aborted by user");
4873
6800
  if (promptKey) {
4874
- setHistory((prev) => __spreadProps(__spreadValues({}, prev), {
4875
- [promptKey]: {
4876
- content: "Response canceled",
4877
- callId: lastCallId || ""
4878
- }
4879
- }));
6801
+ setHistory((prev) => {
6802
+ const existingEntry = prev[promptKey] || { content: "", callId: "" };
6803
+ return __spreadProps(__spreadValues({}, prev), {
6804
+ [promptKey]: __spreadProps(__spreadValues({}, existingEntry), {
6805
+ content: "Response canceled",
6806
+ callId: lastCallId || existingEntry.callId || ""
6807
+ })
6808
+ });
6809
+ });
4880
6810
  }
4881
6811
  } else if (errorMsg.includes("413") || errorMsg.toLowerCase().includes("content too large")) {
4882
6812
  setError({
@@ -4884,12 +6814,15 @@ var AIChatPanel = ({
4884
6814
  code: "413"
4885
6815
  });
4886
6816
  if (promptKey) {
4887
- setHistory((prev) => __spreadProps(__spreadValues({}, prev), {
4888
- [promptKey]: {
4889
- content: `Error: ${errorMsg}`,
4890
- callId: lastCallId || ""
4891
- }
4892
- }));
6817
+ setHistory((prev) => {
6818
+ const existingEntry = prev[promptKey] || { content: "", callId: "" };
6819
+ return __spreadProps(__spreadValues({}, prev), {
6820
+ [promptKey]: __spreadProps(__spreadValues({}, existingEntry), {
6821
+ content: `Error: ${errorMsg}`,
6822
+ callId: lastCallId || existingEntry.callId || ""
6823
+ })
6824
+ });
6825
+ });
4893
6826
  }
4894
6827
  } else if (errorMsg.toLowerCase().includes("network error") || errorMsg.toLowerCase().includes("fetch")) {
4895
6828
  setError({
@@ -4897,12 +6830,15 @@ var AIChatPanel = ({
4897
6830
  code: "NETWORK_ERROR"
4898
6831
  });
4899
6832
  if (promptKey) {
4900
- setHistory((prev) => __spreadProps(__spreadValues({}, prev), {
4901
- [promptKey]: {
4902
- content: `Error: ${errorMsg}`,
4903
- callId: lastCallId || ""
4904
- }
4905
- }));
6833
+ setHistory((prev) => {
6834
+ const existingEntry = prev[promptKey] || { content: "", callId: "" };
6835
+ return __spreadProps(__spreadValues({}, prev), {
6836
+ [promptKey]: __spreadProps(__spreadValues({}, existingEntry), {
6837
+ content: `Error: ${errorMsg}`,
6838
+ callId: lastCallId || existingEntry.callId || ""
6839
+ })
6840
+ });
6841
+ });
4906
6842
  }
4907
6843
  } else {
4908
6844
  setError({
@@ -4910,12 +6846,15 @@ var AIChatPanel = ({
4910
6846
  code: "UNKNOWN_ERROR"
4911
6847
  });
4912
6848
  if (promptKey) {
4913
- setHistory((prev) => __spreadProps(__spreadValues({}, prev), {
4914
- [promptKey]: {
4915
- content: `Error: ${errorMsg}`,
4916
- callId: lastCallId || ""
4917
- }
4918
- }));
6849
+ setHistory((prev) => {
6850
+ const existingEntry = prev[promptKey] || { content: "", callId: "" };
6851
+ return __spreadProps(__spreadValues({}, prev), {
6852
+ [promptKey]: __spreadProps(__spreadValues({}, existingEntry), {
6853
+ content: `Error: ${errorMsg}`,
6854
+ callId: lastCallId || existingEntry.callId || ""
6855
+ })
6856
+ });
6857
+ });
4919
6858
  }
4920
6859
  }
4921
6860
  setIsLoading(false);
@@ -4937,14 +6876,20 @@ var AIChatPanel = ({
4937
6876
  lastCallId,
4938
6877
  processThinkingTags,
4939
6878
  clearFollowOnQuestionsNextPrompt,
4940
- history,
4941
6879
  promptTemplate,
4942
6880
  send,
4943
6881
  service,
6882
+ agent,
4944
6883
  ensureConversation,
6884
+ normalizeHistoryPromptForContext,
6885
+ buildAssistantContextContent,
6886
+ traceContextMode,
4945
6887
  dataWithExtras,
4946
6888
  scrollToBottom,
4947
6889
  onConversationCreated,
6890
+ onBeforeSend,
6891
+ getThinkingBlockCollapseKey,
6892
+ getThinkingBlockRenderKey,
4948
6893
  setResponse
4949
6894
  ]);
4950
6895
  const handleSuggestionClick = (0, import_react14.useCallback)((question) => {
@@ -4972,6 +6917,14 @@ var AIChatPanel = ({
4972
6917
  setLastKey(null);
4973
6918
  setIsLoading(false);
4974
6919
  setCurrentConversation(null);
6920
+ thinkingBlocksByKeyRef.current = {};
6921
+ setThinkingBlocksByKey({});
6922
+ handledToolCallSignaturesRef.current = /* @__PURE__ */ new Set();
6923
+ inFlightToolCallSignaturesRef.current = /* @__PURE__ */ new Set();
6924
+ toolContinuationCountRef.current = 0;
6925
+ activeStreamAppendBaseRef.current = null;
6926
+ toolReplaySummariesByKeyRef.current = {};
6927
+ setPendingToolRequests([]);
4975
6928
  setFollowOnQuestionsState(followOnQuestions);
4976
6929
  setThinkingBlocks([]);
4977
6930
  setCurrentThinkingIndex(0);
@@ -4982,6 +6935,7 @@ var AIChatPanel = ({
4982
6935
  setLastController(new AbortController());
4983
6936
  setUserHasScrolled(false);
4984
6937
  setError(null);
6938
+ setActiveToolCalls([]);
4985
6939
  setTimeout(() => {
4986
6940
  var _a2;
4987
6941
  setJustReset(false);
@@ -4990,43 +6944,85 @@ var AIChatPanel = ({
4990
6944
  }, [newConversationConfirm, idle, stop, lastController, setResponse, followOnQuestions]);
4991
6945
  (0, import_react14.useEffect)(() => {
4992
6946
  if (!response || !lastKey || justReset) return;
4993
- const { cleanedText, completedBlocks } = processThinkingTags(response);
4994
- setThinkingBlocks(completedBlocks);
4995
- if (completedBlocks.length > prevBlockCountRef.current) {
6947
+ const extractedToolRequests = extractToolRequests(response);
6948
+ const seenToolCallSignatures = /* @__PURE__ */ new Set();
6949
+ const unseenToolRequests = extractedToolRequests.filter((request) => {
6950
+ const callSignature = getToolCallSignature(request.toolName, request.callId);
6951
+ if (!callSignature) return false;
6952
+ if (seenToolCallSignatures.has(callSignature)) return false;
6953
+ seenToolCallSignatures.add(callSignature);
6954
+ if (handledToolCallSignaturesRef.current.has(callSignature)) return false;
6955
+ if (inFlightToolCallSignaturesRef.current.has(callSignature)) return false;
6956
+ return true;
6957
+ });
6958
+ setPendingToolRequests((prev) => {
6959
+ if (areToolRequestListsEqual(prev, unseenToolRequests)) {
6960
+ return prev;
6961
+ }
6962
+ return unseenToolRequests;
6963
+ });
6964
+ const responseWithInlineToolLabels = formatToolRequestsForDisplay(response);
6965
+ const { cleanedText: parsedCleanedText, completedBlocks } = processThinkingTags(responseWithInlineToolLabels);
6966
+ const cleanedText = parsedCleanedText.trim();
6967
+ const existingBlocks = thinkingBlocksByKeyRef.current[lastKey] || [];
6968
+ const mergedBlocks = mergeThinkingBlocks(existingBlocks, completedBlocks);
6969
+ thinkingBlocksByKeyRef.current[lastKey] = mergedBlocks;
6970
+ setThinkingBlocksByKey((prev) => {
6971
+ const existing = prev[lastKey];
6972
+ const isSame = !!existing && existing.length === mergedBlocks.length && existing.every(
6973
+ (block, index) => {
6974
+ var _a2, _b;
6975
+ return block.type === ((_a2 = mergedBlocks[index]) == null ? void 0 : _a2.type) && block.content === ((_b = mergedBlocks[index]) == null ? void 0 : _b.content);
6976
+ }
6977
+ );
6978
+ if (isSame) return prev;
6979
+ return __spreadProps(__spreadValues({}, prev), {
6980
+ [lastKey]: mergedBlocks
6981
+ });
6982
+ });
6983
+ setThinkingBlocks(mergedBlocks);
6984
+ setCurrentThinkingIndex(Math.max(0, mergedBlocks.length - 1));
6985
+ if (mergedBlocks.length > prevBlockCountRef.current) {
4996
6986
  setCollapsedBlocks((prev) => {
4997
6987
  const next = new Set(prev);
4998
- for (let i = 0; i < completedBlocks.length - 1; i++) {
4999
- next.add(`block-${i}`);
6988
+ for (let i = 0; i < mergedBlocks.length - 1; i++) {
6989
+ const block = mergedBlocks[i];
6990
+ if (!block) continue;
6991
+ next.add(getThinkingBlockCollapseKey(lastKey, getThinkingBlockRenderKey(block, i)));
5000
6992
  }
5001
6993
  return next;
5002
6994
  });
5003
- prevBlockCountRef.current = completedBlocks.length;
5004
- }
5005
- const hasMainContent = cleanedText.trim().length > 0;
5006
- const hasThinkingContent = completedBlocks.length > 0 || processThinkingTags(response).activeBlock !== null;
5007
- if (hasMainContent && hasThinkingContent && !hasAutoCollapsedRef.current) {
5008
- hasAutoCollapsedRef.current = true;
5009
- setTimeout(() => {
5010
- setCollapsedBlocks((prev) => {
5011
- const next = new Set(prev);
5012
- completedBlocks.forEach((_, index) => next.add(`block-${index}`));
5013
- next.add("active");
5014
- return next;
5015
- });
5016
- }, 500);
6995
+ prevBlockCountRef.current = mergedBlocks.length;
5017
6996
  }
5018
6997
  setHistory((prev) => {
5019
6998
  const newHistory = __spreadValues({}, prev);
5020
- newHistory[lastKey] = {
5021
- content: cleanedText,
5022
- // Store raw content, not processed
5023
- callId: lastCallId || ""
5024
- };
6999
+ const existingEntry = newHistory[lastKey] || { content: "", callId: "" };
7000
+ const appendBase = activeStreamAppendBaseRef.current;
7001
+ const mergedContinuationContent = appendBase && appendBase.key === lastKey ? mergeContinuationResponseText(appendBase.base, cleanedText) : cleanedText;
7002
+ const existingContent = typeof existingEntry.content === "string" ? existingEntry.content : "";
7003
+ const nextContent = appendBase && appendBase.key === lastKey && existingContent.length > mergedContinuationContent.length ? existingContent : shouldPreserveBoundaryDroppedStreamText(existingContent, mergedContinuationContent) ? existingContent : mergedContinuationContent;
7004
+ newHistory[lastKey] = __spreadProps(__spreadValues({}, existingEntry), {
7005
+ content: nextContent,
7006
+ // Store raw content without tool JSON or thinking tags
7007
+ callId: lastCallId || existingEntry.callId || ""
7008
+ });
5025
7009
  latestHistoryRef.current = newHistory;
5026
7010
  return newHistory;
5027
7011
  });
5028
- }, [response, lastKey, lastCallId, processThinkingTags, justReset]);
7012
+ }, [
7013
+ response,
7014
+ lastKey,
7015
+ lastCallId,
7016
+ processThinkingTags,
7017
+ justReset,
7018
+ extractToolRequests,
7019
+ getToolCallSignature,
7020
+ getThinkingBlockCollapseKey,
7021
+ getThinkingBlockRenderKey,
7022
+ formatToolRequestsForDisplay
7023
+ ]);
5029
7024
  (0, import_react14.useEffect)(() => {
7025
+ var _a2;
5030
7026
  const wasStreaming = !prevIdleRef.current;
5031
7027
  const isNowIdle = idle;
5032
7028
  prevIdleRef.current = idle;
@@ -5050,6 +7046,12 @@ var AIChatPanel = ({
5050
7046
  if (!isNowIdle && hasNotifiedCompletionRef.current) {
5051
7047
  hasNotifiedCompletionRef.current = false;
5052
7048
  prevResponseLengthRef.current = 0;
7049
+ const currentLastKey = lastKeyRef.current;
7050
+ const existingContent = currentLastKey ? ((_a2 = latestHistoryRef.current[currentLastKey]) == null ? void 0 : _a2.content) || "" : "";
7051
+ activeStreamAppendBaseRef.current = currentLastKey && existingContent.trim().length > 0 ? {
7052
+ key: currentLastKey,
7053
+ base: existingContent
7054
+ } : null;
5053
7055
  }
5054
7056
  }, [idle]);
5055
7057
  (0, import_react14.useEffect)(() => {
@@ -5070,21 +7072,19 @@ var AIChatPanel = ({
5070
7072
  scrollToBottom(true);
5071
7073
  }
5072
7074
  }, [response, idle]);
5073
- const idleRef = (0, import_react14.useRef)(idle);
5074
- idleRef.current = idle;
5075
7075
  (0, import_react14.useEffect)(() => {
5076
7076
  const scrollArea = responseAreaRef.current;
5077
7077
  if (!scrollArea) return;
5078
7078
  const scrollViewport = scrollArea.querySelector(".ai-scroll-area-viewport");
5079
7079
  const scrollElement = scrollViewport || scrollArea;
5080
7080
  const handleWheel = (e) => {
5081
- if (idleRef.current) return;
7081
+ if (streamIdleRef.current) return;
5082
7082
  if (e.deltaY < 0 && !userHasScrolledRef.current) {
5083
7083
  setUserHasScrolled(true);
5084
7084
  }
5085
7085
  };
5086
7086
  const handleScroll = () => {
5087
- if (idleRef.current || !userHasScrolledRef.current) return;
7087
+ if (streamIdleRef.current || !userHasScrolledRef.current) return;
5088
7088
  const scrollHeight = scrollElement.scrollHeight;
5089
7089
  const currentScrollTop = scrollElement.scrollTop;
5090
7090
  const clientHeight = scrollElement.clientHeight;
@@ -5101,7 +7101,12 @@ var AIChatPanel = ({
5101
7101
  };
5102
7102
  }, []);
5103
7103
  (0, import_react14.useEffect)(() => {
5104
- setFollowOnQuestionsState(followOnQuestions);
7104
+ setFollowOnQuestionsState((prev) => {
7105
+ if (prev.length === followOnQuestions.length && prev.every((question, index) => question === followOnQuestions[index])) {
7106
+ return prev;
7107
+ }
7108
+ return followOnQuestions;
7109
+ });
5105
7110
  }, [followOnQuestions]);
5106
7111
  (0, import_react14.useEffect)(() => {
5107
7112
  const currentlyLoading = isLoading || !idle;
@@ -5119,10 +7124,11 @@ var AIChatPanel = ({
5119
7124
  const currentLastKey = lastKeyRef.current;
5120
7125
  const currentLastCallId = lastCallIdRef.current;
5121
7126
  if (currentLastKey && currentResponse) {
5122
- currentHistory[currentLastKey] = {
7127
+ const existingEntry = currentHistory[currentLastKey] || { content: "", callId: "" };
7128
+ currentHistory[currentLastKey] = __spreadProps(__spreadValues({}, existingEntry), {
5123
7129
  content: currentResponse + "\n\n(response interrupted)",
5124
- callId: currentLastCallId || ""
5125
- };
7130
+ callId: currentLastCallId || existingEntry.callId || ""
7131
+ });
5126
7132
  }
5127
7133
  if (historyCallbackRef.current && Object.keys(currentHistory).length > 0 && !hasNotifiedCompletionRef.current) {
5128
7134
  historyCallbackRef.current(currentHistory);
@@ -5159,12 +7165,15 @@ var AIChatPanel = ({
5159
7165
  code: "413"
5160
7166
  });
5161
7167
  if (lastKey) {
5162
- setHistory((prev) => __spreadProps(__spreadValues({}, prev), {
5163
- [lastKey]: {
5164
- content: `Error: ${errorMessage}`,
5165
- callId: lastCallId || ""
5166
- }
5167
- }));
7168
+ setHistory((prev) => {
7169
+ const existingEntry = prev[lastKey] || { content: "", callId: "" };
7170
+ return __spreadProps(__spreadValues({}, prev), {
7171
+ [lastKey]: __spreadProps(__spreadValues({}, existingEntry), {
7172
+ content: `Error: ${errorMessage}`,
7173
+ callId: lastCallId || existingEntry.callId || ""
7174
+ })
7175
+ });
7176
+ });
5168
7177
  }
5169
7178
  } else if (errorMessage.toLowerCase().includes("network error") || errorMessage.toLowerCase().includes("fetch")) {
5170
7179
  setError({
@@ -5172,12 +7181,15 @@ var AIChatPanel = ({
5172
7181
  code: "NETWORK_ERROR"
5173
7182
  });
5174
7183
  if (lastKey) {
5175
- setHistory((prev) => __spreadProps(__spreadValues({}, prev), {
5176
- [lastKey]: {
5177
- content: `Error: ${errorMessage}`,
5178
- callId: lastCallId || ""
5179
- }
5180
- }));
7184
+ setHistory((prev) => {
7185
+ const existingEntry = prev[lastKey] || { content: "", callId: "" };
7186
+ return __spreadProps(__spreadValues({}, prev), {
7187
+ [lastKey]: __spreadProps(__spreadValues({}, existingEntry), {
7188
+ content: `Error: ${errorMessage}`,
7189
+ callId: lastCallId || existingEntry.callId || ""
7190
+ })
7191
+ });
7192
+ });
5181
7193
  }
5182
7194
  } else {
5183
7195
  setError({
@@ -5185,12 +7197,15 @@ var AIChatPanel = ({
5185
7197
  code: "UNKNOWN_ERROR"
5186
7198
  });
5187
7199
  if (lastKey) {
5188
- setHistory((prev) => __spreadProps(__spreadValues({}, prev), {
5189
- [lastKey]: {
5190
- content: `Error: ${errorMessage}`,
5191
- callId: lastCallId || ""
5192
- }
5193
- }));
7200
+ setHistory((prev) => {
7201
+ const existingEntry = prev[lastKey] || { content: "", callId: "" };
7202
+ return __spreadProps(__spreadValues({}, prev), {
7203
+ [lastKey]: __spreadProps(__spreadValues({}, existingEntry), {
7204
+ content: `Error: ${errorMessage}`,
7205
+ callId: lastCallId || existingEntry.callId || ""
7206
+ })
7207
+ });
7208
+ });
5194
7209
  }
5195
7210
  }
5196
7211
  setIsLoading(false);
@@ -5321,47 +7336,220 @@ var AIChatPanel = ({
5321
7336
  );
5322
7337
  }
5323
7338
  }), [CodeBlock, AgentSuggestionCard]);
5324
- const renderThinkingBlocks = (0, import_react14.useCallback)((isStreaming = false) => {
5325
- const hasActiveBlock = activeThinkingBlock !== null;
5326
- const hasCompletedBlocks = thinkingBlocks.length > 0;
5327
- if (!hasActiveBlock && !hasCompletedBlocks) return null;
5328
- const handleToggleCollapse = (blockKey) => {
5329
- setCollapsedBlocks((prev) => {
5330
- const next = new Set(prev);
5331
- if (next.has(blockKey)) {
5332
- next.delete(blockKey);
5333
- } else {
5334
- next.add(blockKey);
7339
+ const toggleThinkingBlockCollapsed = (0, import_react14.useCallback)((entryKey, blockKey) => {
7340
+ const collapseKey = getThinkingBlockCollapseKey(entryKey, blockKey);
7341
+ setCollapsedBlocks((prev) => {
7342
+ const next = new Set(prev);
7343
+ if (next.has(collapseKey)) {
7344
+ next.delete(collapseKey);
7345
+ } else {
7346
+ next.add(collapseKey);
7347
+ }
7348
+ return next;
7349
+ });
7350
+ }, [getThinkingBlockCollapseKey]);
7351
+ const renderThinkingBlockCard = (entryKey, block, blockKey, renderKey, isStreaming) => {
7352
+ const collapseKey = getThinkingBlockCollapseKey(entryKey, blockKey);
7353
+ return /* @__PURE__ */ import_react14.default.createElement(
7354
+ ThinkingBlock,
7355
+ {
7356
+ key: renderKey,
7357
+ type: block.type,
7358
+ content: block.content,
7359
+ isStreaming,
7360
+ isCollapsed: collapsedBlocks.has(collapseKey),
7361
+ onToggleCollapse: () => toggleThinkingBlockCollapsed(entryKey, blockKey)
7362
+ }
7363
+ );
7364
+ };
7365
+ const renderActiveThinkingBlock = (entryKey, activeBlock, keyPrefix) => {
7366
+ if (!activeBlock) return null;
7367
+ return /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-inline-thinking-events" }, renderThinkingBlockCard(entryKey, activeBlock, "active", `${keyPrefix}-active`, true));
7368
+ };
7369
+ const panelClasses = ["ai-chat-panel", theme === "dark" ? "dark-theme" : ""].filter(Boolean).join(" ");
7370
+ const getToolStatusRank = (status) => {
7371
+ switch (status) {
7372
+ case "error":
7373
+ return 4;
7374
+ case "completed":
7375
+ return 3;
7376
+ case "running":
7377
+ return 2;
7378
+ case "pending":
7379
+ default:
7380
+ return 1;
7381
+ }
7382
+ };
7383
+ const formatToolCallId = (callId) => {
7384
+ const normalized = String(callId || "").trim();
7385
+ if (normalized.length <= 22) return normalized;
7386
+ return `${normalized.slice(0, 10)}...${normalized.slice(-8)}`;
7387
+ };
7388
+ const renderMarkdownContent = (content, key) => {
7389
+ if (!content || !content.trim()) return null;
7390
+ if (markdownClass) {
7391
+ return /* @__PURE__ */ import_react14.default.createElement("div", { key, className: markdownClass }, /* @__PURE__ */ import_react14.default.createElement(
7392
+ import_react_markdown2.default,
7393
+ {
7394
+ remarkPlugins: [import_remark_gfm2.default],
7395
+ rehypePlugins: [import_rehype_raw2.default],
7396
+ components: markdownComponents
7397
+ },
7398
+ content
7399
+ ));
7400
+ }
7401
+ return /* @__PURE__ */ import_react14.default.createElement(
7402
+ import_react_markdown2.default,
7403
+ {
7404
+ key,
7405
+ remarkPlugins: [import_remark_gfm2.default],
7406
+ rehypePlugins: [import_rehype_raw2.default],
7407
+ components: markdownComponents
7408
+ },
7409
+ content
7410
+ );
7411
+ };
7412
+ const renderToolStatusRow = (toolStatusRow, key) => /* @__PURE__ */ import_react14.default.createElement(
7413
+ "div",
7414
+ {
7415
+ key,
7416
+ className: `ai-chat-tool-status-row ai-chat-tool-status-row--${toolStatusRow.status}`
7417
+ },
7418
+ /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-tool-status-row__main" }, /* @__PURE__ */ import_react14.default.createElement(ToolIcon, null), /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-tool-status-row__label" }, toolStatusRow.statusLabel), /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-tool-status-row__call-id" }, formatToolCallId(toolStatusRow.callId))),
7419
+ toolStatusRow.status === "pending" && /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-tool-status-row__actions" }, /* @__PURE__ */ import_react14.default.createElement(
7420
+ Button,
7421
+ {
7422
+ size: "sm",
7423
+ variant: "ghost",
7424
+ className: "ai-chat-tool-status-row__button",
7425
+ onClick: () => handleToolApproval(toolStatusRow.toolName, "once")
7426
+ },
7427
+ "once"
7428
+ ), /* @__PURE__ */ import_react14.default.createElement(
7429
+ Button,
7430
+ {
7431
+ size: "sm",
7432
+ variant: "ghost",
7433
+ className: "ai-chat-tool-status-row__button",
7434
+ onClick: () => handleToolApproval(toolStatusRow.toolName, "session")
7435
+ },
7436
+ "session"
7437
+ ), /* @__PURE__ */ import_react14.default.createElement(
7438
+ Button,
7439
+ {
7440
+ size: "sm",
7441
+ variant: "ghost",
7442
+ className: "ai-chat-tool-status-row__button",
7443
+ onClick: () => handleToolApproval(toolStatusRow.toolName, "always")
7444
+ },
7445
+ "always"
7446
+ ))
7447
+ );
7448
+ const renderContentWithInlineToolCards = (content, toolStatusRows, thinkingBlocksForEntry, entryKey, keyPrefix) => {
7449
+ const { parts, markers } = parseInlineToolMarkers(content);
7450
+ const thinkingBlocksBySignature = /* @__PURE__ */ new Map();
7451
+ thinkingBlocksForEntry.forEach((block, index) => {
7452
+ const signature = String((block == null ? void 0 : block.signature) || "").trim() || getThinkingBlockRenderKey(block, index);
7453
+ if (!thinkingBlocksBySignature.has(signature)) {
7454
+ thinkingBlocksBySignature.set(signature, __spreadProps(__spreadValues({}, block), {
7455
+ signature
7456
+ }));
7457
+ }
7458
+ });
7459
+ const pendingBySignature = /* @__PURE__ */ new Map();
7460
+ const pendingByCallId = /* @__PURE__ */ new Map();
7461
+ toolStatusRows.forEach((row) => {
7462
+ if (!pendingBySignature.has(row.signature)) {
7463
+ pendingBySignature.set(row.signature, row);
7464
+ }
7465
+ if (!pendingByCallId.has(row.callId)) {
7466
+ pendingByCallId.set(row.callId, row);
7467
+ }
7468
+ });
7469
+ const nodes = [];
7470
+ parts.forEach((part, partIndex) => {
7471
+ const { parts: thinkingParts, markers: thinkingMarkers } = parseInlineThinkingMarkers(part);
7472
+ thinkingParts.forEach((thinkingPart, thinkingIndex) => {
7473
+ const markdownNode = renderMarkdownContent(
7474
+ thinkingPart,
7475
+ `${keyPrefix}-md-${partIndex}-${thinkingIndex}`
7476
+ );
7477
+ if (markdownNode) {
7478
+ nodes.push(markdownNode);
5335
7479
  }
5336
- return next;
7480
+ const thinkingMarker = thinkingMarkers[thinkingIndex];
7481
+ if (!thinkingMarker) return;
7482
+ const matchedBlock = thinkingBlocksBySignature.get(thinkingMarker.signature);
7483
+ if (!matchedBlock) return;
7484
+ const blockKey = getThinkingBlockRenderKey(matchedBlock, thinkingIndex);
7485
+ nodes.push(
7486
+ /* @__PURE__ */ import_react14.default.createElement(
7487
+ "div",
7488
+ {
7489
+ key: `${keyPrefix}-thinking-${partIndex}-${thinkingIndex}-${blockKey}`,
7490
+ className: "ai-chat-inline-thinking-events"
7491
+ },
7492
+ renderThinkingBlockCard(
7493
+ entryKey,
7494
+ matchedBlock,
7495
+ blockKey,
7496
+ `${keyPrefix}-thinking-card-${partIndex}-${thinkingIndex}-${blockKey}`,
7497
+ false
7498
+ )
7499
+ )
7500
+ );
5337
7501
  });
5338
- };
5339
- return /* @__PURE__ */ import_react14.default.createElement(import_react14.default.Fragment, null, thinkingBlocks.map((block, index) => {
5340
- const blockKey = `block-${index}`;
5341
- return /* @__PURE__ */ import_react14.default.createElement(
5342
- ThinkingBlock,
5343
- {
5344
- key: blockKey,
5345
- type: block.type,
5346
- content: block.content,
5347
- isStreaming: false,
5348
- isCollapsed: collapsedBlocks.has(blockKey),
5349
- onToggleCollapse: () => handleToggleCollapse(blockKey)
7502
+ const marker = markers[partIndex];
7503
+ if (!marker) return;
7504
+ const markerSignature = getToolCallSignature(marker.toolName, marker.callId);
7505
+ const matchedRow = (markerSignature ? pendingBySignature.get(markerSignature) : void 0) || pendingByCallId.get(marker.callId);
7506
+ const fallbackSignature = markerSignature || getToolCallSignature(marker.toolName, `${marker.callId}-${partIndex + 1}`) || `${marker.toolName}::${marker.callId}::${partIndex}`;
7507
+ const rowToRender = matchedRow || {
7508
+ signature: fallbackSignature,
7509
+ toolName: marker.toolName,
7510
+ callId: marker.callId,
7511
+ status: "running",
7512
+ statusLabel: `tool call ${marker.toolName} running`
7513
+ };
7514
+ if (matchedRow) {
7515
+ pendingBySignature.delete(matchedRow.signature);
7516
+ const pendingByCallIdMatch = pendingByCallId.get(matchedRow.callId);
7517
+ if ((pendingByCallIdMatch == null ? void 0 : pendingByCallIdMatch.signature) === matchedRow.signature) {
7518
+ pendingByCallId.delete(matchedRow.callId);
5350
7519
  }
5351
- );
5352
- }), activeThinkingBlock && /* @__PURE__ */ import_react14.default.createElement(
5353
- ThinkingBlock,
5354
- {
5355
- key: "active-streaming",
5356
- type: activeThinkingBlock.type,
5357
- content: activeThinkingBlock.content,
5358
- isStreaming: true,
5359
- isCollapsed: collapsedBlocks.has("active"),
5360
- onToggleCollapse: () => handleToggleCollapse("active")
5361
7520
  }
5362
- ));
5363
- }, [thinkingBlocks, activeThinkingBlock, collapsedBlocks]);
5364
- const panelClasses = ["ai-chat-panel", theme === "dark" ? "dark-theme" : ""].filter(Boolean).join(" ");
7521
+ nodes.push(
7522
+ /* @__PURE__ */ import_react14.default.createElement(
7523
+ "div",
7524
+ {
7525
+ key: `${keyPrefix}-inline-${rowToRender.signature}-${partIndex}`,
7526
+ className: "ai-chat-inline-tool-events",
7527
+ role: "status",
7528
+ "aria-live": "polite"
7529
+ },
7530
+ renderToolStatusRow(rowToRender, `${keyPrefix}-row-${rowToRender.signature}-${partIndex}`)
7531
+ )
7532
+ );
7533
+ });
7534
+ const unmatchedRows = Array.from(pendingBySignature.values());
7535
+ if (unmatchedRows.length > 0) {
7536
+ nodes.push(
7537
+ /* @__PURE__ */ import_react14.default.createElement(
7538
+ "div",
7539
+ {
7540
+ key: `${keyPrefix}-tail`,
7541
+ className: "ai-chat-inline-tool-events ai-chat-inline-tool-events--tail",
7542
+ role: "status",
7543
+ "aria-live": "polite"
7544
+ },
7545
+ unmatchedRows.map(
7546
+ (row, rowIndex) => renderToolStatusRow(row, `${keyPrefix}-tail-${row.signature}-${rowIndex}`)
7547
+ )
7548
+ )
7549
+ );
7550
+ }
7551
+ return /* @__PURE__ */ import_react14.default.createElement(import_react14.default.Fragment, null, nodes);
7552
+ };
5365
7553
  return /* @__PURE__ */ import_react14.default.createElement(
5366
7554
  "div",
5367
7555
  {
@@ -5394,47 +7582,114 @@ var AIChatPanel = ({
5394
7582
  initialMessage
5395
7583
  ))), Object.entries(history).map(([prompt, entry], index, entries) => {
5396
7584
  const isLastEntry = index === entries.length - 1;
7585
+ const isStreamingEntry = isLastEntry && (isLoading || !idle) && !justReset;
7586
+ const continuationAppendBase = isStreamingEntry && activeStreamAppendBaseRef.current && activeStreamAppendBaseRef.current.key === prompt && activeStreamAppendBaseRef.current.base.trim().length > 0 ? activeStreamAppendBaseRef.current : null;
7587
+ const isContinuationStreamingEntry = !!continuationAppendBase;
5397
7588
  const isSystemMessage = prompt.startsWith("__system__:");
5398
7589
  const { cleanedText } = processThinkingTags(entry.content);
5399
7590
  const processedContent = processActions(cleanedText);
5400
- return /* @__PURE__ */ import_react14.default.createElement("div", { key: index, className: "ai-chat-entry" }, !(hideInitialPrompt && index === 0) && !isSystemMessage && /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-message ai-chat-message--user" }, /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-message__content" }, formatPromptForDisplay(prompt))), /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-message ai-chat-message--assistant" }, /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-message__content" }, isLastEntry && (isLoading || !idle) && !justReset ? (() => {
5401
- const { cleanedText: streamingCleanedText } = processThinkingTags(response || "");
5402
- const streamingContent = processActions(streamingCleanedText);
7591
+ const entryThinkingBlocks = thinkingBlocksByKey[prompt] || [];
7592
+ const statusBySignature = /* @__PURE__ */ new Map();
7593
+ const upsertToolStatus = (row) => {
7594
+ const existing = statusBySignature.get(row.signature);
7595
+ if (!existing || getToolStatusRank(row.status) >= getToolStatusRank(existing.status)) {
7596
+ statusBySignature.set(row.signature, row);
7597
+ }
7598
+ };
7599
+ const entryToolCalls = Array.isArray(entry.toolCalls) ? entry.toolCalls : [];
7600
+ const entryToolResponses = Array.isArray(entry.toolResponses) ? entry.toolResponses : [];
7601
+ entryToolCalls.forEach((toolCall, toolIndex) => {
7602
+ const toolName = String((toolCall == null ? void 0 : toolCall.name) || "").trim() || "tool";
7603
+ const callId = String((toolCall == null ? void 0 : toolCall.id) || "").trim() || `${toolName}-${toolIndex + 1}`;
7604
+ const signature = getToolCallSignature(toolName, callId) || `completed-${prompt}-${toolIndex}-${toolName}-${callId}`;
7605
+ const matchedResponse = entryToolResponses.find((response2) => String((response2 == null ? void 0 : response2.tool_call_id) || "").trim() === callId) || entryToolResponses[toolIndex];
7606
+ const isError = Boolean(matchedResponse == null ? void 0 : matchedResponse.isError);
7607
+ upsertToolStatus({
7608
+ signature,
7609
+ toolName,
7610
+ callId,
7611
+ status: isError ? "error" : "completed",
7612
+ statusLabel: isError ? `tool call ${toolName} errored` : `tool call ${toolName} completed`
7613
+ });
7614
+ });
7615
+ if (isLastEntry && !justReset) {
7616
+ pendingToolRequests.forEach((request, requestIndex) => {
7617
+ const toolName = String((request == null ? void 0 : request.toolName) || "").trim() || "tool";
7618
+ const callId = String((request == null ? void 0 : request.callId) || "").trim() || `${toolName}-pending-${requestIndex + 1}`;
7619
+ const signature = getToolCallSignature(toolName, callId) || `pending-${prompt}-${requestIndex}-${toolName}-${callId}`;
7620
+ upsertToolStatus({
7621
+ signature,
7622
+ toolName,
7623
+ callId,
7624
+ status: "pending",
7625
+ statusLabel: `tool call ${toolName} awaiting approval`
7626
+ });
7627
+ });
7628
+ activeToolCalls.forEach((activeToolCall, activeIndex) => {
7629
+ const toolName = String((activeToolCall == null ? void 0 : activeToolCall.toolName) || "").trim() || "tool";
7630
+ const callId = String((activeToolCall == null ? void 0 : activeToolCall.callId) || "").trim() || `${toolName}-running-${activeIndex + 1}`;
7631
+ const signature = getToolCallSignature(toolName, callId) || `running-${prompt}-${activeIndex}-${toolName}-${callId}`;
7632
+ upsertToolStatus({
7633
+ signature,
7634
+ toolName,
7635
+ callId,
7636
+ status: "running",
7637
+ statusLabel: `tool call ${toolName} running`
7638
+ });
7639
+ });
7640
+ }
7641
+ const entryToolStatusRows = Array.from(statusBySignature.values());
7642
+ const hasInFlightToolStatus = entryToolStatusRows.some(
7643
+ (row) => row.status === "pending" || row.status === "running"
7644
+ );
7645
+ const isActivePromptEntry = !!lastKey && prompt === lastKey;
7646
+ const shouldShowBusyGapThinkingFallback = isActivePromptEntry && !isStreamingEntry && (isLoading || !idle) && !activeThinkingBlock && !hasInFlightToolStatus;
7647
+ return /* @__PURE__ */ import_react14.default.createElement("div", { key: prompt, className: "ai-chat-entry" }, !(hideInitialPrompt && index === 0) && !isSystemMessage && /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-message ai-chat-message--user" }, /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-message__content" }, formatPromptForDisplay(prompt))), /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-message ai-chat-message--assistant" }, /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-message__content" }, isStreamingEntry ? (() => {
7648
+ if (isContinuationStreamingEntry) {
7649
+ const continuationResponseWithInlineToolLabels = formatToolRequestsForDisplay(response || "");
7650
+ const { cleanedText: continuationCleanedText } = processThinkingTags(
7651
+ continuationResponseWithInlineToolLabels
7652
+ );
7653
+ const appendBase2 = continuationAppendBase || activeStreamAppendBaseRef.current;
7654
+ const continuationMergedText = appendBase2 && appendBase2.key === prompt ? mergeContinuationResponseText(appendBase2.base, continuationCleanedText.trim()) : continuationCleanedText;
7655
+ const continuationDisplaySource = continuationMergedText.trim().length > 0 ? continuationMergedText : cleanedText;
7656
+ const continuationDisplayContent = processActions(continuationDisplaySource);
7657
+ const hasVisibleContinuationContent = continuationDisplayContent.trim().length > 0;
7658
+ const hasFreshContinuationContent = continuationCleanedText.trim().length > 0;
7659
+ const showThinkingFallback2 = !activeThinkingBlock && !hasFreshContinuationContent && !hasInFlightToolStatus;
7660
+ return /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-streaming" }, renderActiveThinkingBlock(prompt, activeThinkingBlock, `${prompt}-continuation`), hasVisibleContinuationContent ? /* @__PURE__ */ import_react14.default.createElement(import_react14.default.Fragment, null, renderContentWithInlineToolCards(
7661
+ continuationDisplayContent,
7662
+ entryToolStatusRows,
7663
+ entryThinkingBlocks,
7664
+ prompt,
7665
+ `${prompt}-continuation`
7666
+ ), showThinkingFallback2 && /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-loading ai-chat-loading--inline" }, /* @__PURE__ */ import_react14.default.createElement("span", null, "Thinking"), /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-loading__dots" }, /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-loading__dot" }), /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-loading__dot" }), /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-loading__dot" })))) : /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-loading" }, /* @__PURE__ */ import_react14.default.createElement("span", null, "Continuing response"), /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-loading__dots" }, /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-loading__dot" }), /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-loading__dot" }), /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-loading__dot" }))));
7667
+ }
7668
+ const streamingResponseWithInlineToolLabels = formatToolRequestsForDisplay(response || "");
7669
+ const { cleanedText: streamingCleanedText } = processThinkingTags(streamingResponseWithInlineToolLabels);
7670
+ const appendBase = activeStreamAppendBaseRef.current;
7671
+ const streamingMergedText = appendBase && appendBase.key === prompt ? mergeContinuationResponseText(appendBase.base, streamingCleanedText.trim()) : streamingCleanedText;
7672
+ const streamingContent = processActions(streamingMergedText);
5403
7673
  const hasStreamingContent = streamingContent.trim().length > 0;
5404
- return /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-streaming" }, (thinkingBlocks.length > 0 || activeThinkingBlock) && renderThinkingBlocks(true), hasStreamingContent ? markdownClass ? /* @__PURE__ */ import_react14.default.createElement("div", { className: markdownClass }, /* @__PURE__ */ import_react14.default.createElement(
5405
- import_react_markdown2.default,
5406
- {
5407
- remarkPlugins: [import_remark_gfm2.default],
5408
- rehypePlugins: [import_rehype_raw2.default],
5409
- components: markdownComponents
5410
- },
5411
- streamingContent
5412
- )) : /* @__PURE__ */ import_react14.default.createElement(
5413
- import_react_markdown2.default,
5414
- {
5415
- remarkPlugins: [import_remark_gfm2.default],
5416
- rehypePlugins: [import_rehype_raw2.default],
5417
- components: markdownComponents
5418
- },
5419
- streamingContent
5420
- ) : /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-loading" }, /* @__PURE__ */ import_react14.default.createElement("span", null, thinkingBlocks.length > 0 || activeThinkingBlock ? "Still thinking" : "Thinking"), /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-loading__dots" }, /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-loading__dot" }), /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-loading__dot" }), /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-loading__dot" }))));
5421
- })() : /* @__PURE__ */ import_react14.default.createElement(import_react14.default.Fragment, null, isLastEntry && thinkingBlocks.length > 0 && renderThinkingBlocks(false), markdownClass ? /* @__PURE__ */ import_react14.default.createElement("div", { className: markdownClass }, /* @__PURE__ */ import_react14.default.createElement(
5422
- import_react_markdown2.default,
5423
- {
5424
- remarkPlugins: [import_remark_gfm2.default],
5425
- rehypePlugins: [import_rehype_raw2.default],
5426
- components: markdownComponents
5427
- },
5428
- processedContent
5429
- )) : /* @__PURE__ */ import_react14.default.createElement(
5430
- import_react_markdown2.default,
5431
- {
5432
- remarkPlugins: [import_remark_gfm2.default],
5433
- rehypePlugins: [import_rehype_raw2.default],
5434
- components: markdownComponents
5435
- },
5436
- processedContent
5437
- ))), (!isLastEntry || !isLoading) && /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-message__actions" }, /* @__PURE__ */ import_react14.default.createElement(
7674
+ const hasFreshStreamingContent = streamingCleanedText.trim().length > 0;
7675
+ const fallbackHistoryContent = processedContent.trim();
7676
+ const streamingDisplayContent = hasStreamingContent ? streamingContent : fallbackHistoryContent;
7677
+ const hasDisplayContent = streamingDisplayContent.trim().length > 0;
7678
+ const showThinkingFallback = !activeThinkingBlock && !hasFreshStreamingContent && !hasInFlightToolStatus;
7679
+ return /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-streaming" }, renderActiveThinkingBlock(prompt, activeThinkingBlock, `${prompt}-streaming`), hasDisplayContent ? /* @__PURE__ */ import_react14.default.createElement(import_react14.default.Fragment, null, renderContentWithInlineToolCards(
7680
+ streamingDisplayContent,
7681
+ entryToolStatusRows,
7682
+ entryThinkingBlocks,
7683
+ prompt,
7684
+ `${prompt}-streaming`
7685
+ ), showThinkingFallback && /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-loading ai-chat-loading--inline" }, /* @__PURE__ */ import_react14.default.createElement("span", null, "Thinking"), /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-loading__dots" }, /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-loading__dot" }), /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-loading__dot" }), /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-loading__dot" })))) : /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-loading" }, /* @__PURE__ */ import_react14.default.createElement("span", null, activeThinkingBlock ? "Still thinking" : "Thinking"), /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-loading__dots" }, /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-loading__dot" }), /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-loading__dot" }), /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-loading__dot" }))));
7686
+ })() : /* @__PURE__ */ import_react14.default.createElement(import_react14.default.Fragment, null, renderContentWithInlineToolCards(
7687
+ processedContent,
7688
+ entryToolStatusRows,
7689
+ entryThinkingBlocks,
7690
+ prompt,
7691
+ `${prompt}-final`
7692
+ ), shouldShowBusyGapThinkingFallback && /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-loading ai-chat-loading--inline", "aria-live": "polite" }, /* @__PURE__ */ import_react14.default.createElement("span", null, "Thinking"), /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-loading__dots" }, /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-loading__dot" }), /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-loading__dot" }), /* @__PURE__ */ import_react14.default.createElement("span", { className: "ai-chat-loading__dot" }))))), (!isLastEntry || !isLoading) && /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-message__actions" }, /* @__PURE__ */ import_react14.default.createElement(
5438
7693
  "button",
5439
7694
  {
5440
7695
  className: "ai-chat-action-button",
@@ -5460,7 +7715,7 @@ var AIChatPanel = ({
5460
7715
  style: (feedbackCallId == null ? void 0 : feedbackCallId.callId) === entry.callId && (feedbackCallId == null ? void 0 : feedbackCallId.type) === "down" ? { color: "#ef4444" } : void 0
5461
7716
  },
5462
7717
  (feedbackCallId == null ? void 0 : feedbackCallId.callId) === entry.callId && (feedbackCallId == null ? void 0 : feedbackCallId.type) === "down" ? /* @__PURE__ */ import_react14.default.createElement("span", { style: { fontSize: "11px", fontWeight: 500 } }, "Thanks!") : /* @__PURE__ */ import_react14.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "ai-chat-icon-sm" }, /* @__PURE__ */ import_react14.default.createElement("path", { d: "M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17" }))
5463
- ), (entry.toolCalls || entry.toolResponses) && /* @__PURE__ */ import_react14.default.createElement(
7718
+ ), /* @__PURE__ */ import_react14.default.createElement(
5464
7719
  "button",
5465
7720
  {
5466
7721
  className: "ai-chat-action-button",
@@ -5486,34 +7741,6 @@ var AIChatPanel = ({
5486
7741
  },
5487
7742
  question
5488
7743
  ))), /* @__PURE__ */ import_react14.default.createElement("div", { ref: bottomRef })),
5489
- pendingToolRequests.length > 0 && /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-approve-tools-panel" }, /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-approve-tools-header" }, "Tool Approval Required"), /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-approve-tools-description" }, "The AI wants to use the following tools:"), getUniqueToolNames().map((toolName) => /* @__PURE__ */ import_react14.default.createElement("div", { key: toolName, className: "ai-chat-approve-tool-item" }, /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-approve-tool-name" }, toolName), /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-approve-tools-buttons" }, /* @__PURE__ */ import_react14.default.createElement(
5490
- Button,
5491
- {
5492
- size: "sm",
5493
- variant: "outline",
5494
- className: "ai-chat-approve-tools-button",
5495
- onClick: () => handleToolApproval(toolName, "once")
5496
- },
5497
- "Once"
5498
- ), /* @__PURE__ */ import_react14.default.createElement(
5499
- Button,
5500
- {
5501
- size: "sm",
5502
- variant: "outline",
5503
- className: "ai-chat-approve-tools-button",
5504
- onClick: () => handleToolApproval(toolName, "session")
5505
- },
5506
- "This Session"
5507
- ), /* @__PURE__ */ import_react14.default.createElement(
5508
- Button,
5509
- {
5510
- size: "sm",
5511
- variant: "default",
5512
- className: "ai-chat-approve-tools-button",
5513
- onClick: () => handleToolApproval(toolName, "always")
5514
- },
5515
- "Always"
5516
- ))))),
5517
7744
  (showSaveButton || showEmailButton || showCallToAction) && /* @__PURE__ */ import_react14.default.createElement("div", { className: "ai-chat-button-container" }, showSaveButton && /* @__PURE__ */ import_react14.default.createElement(
5518
7745
  Button,
5519
7746
  {
@@ -5649,7 +7876,7 @@ var AIChatPanel = ({
5649
7876
  ChatInput,
5650
7877
  {
5651
7878
  placeholder,
5652
- idle,
7879
+ isBusy: isLoading || !idle,
5653
7880
  onSubmit: continueChat,
5654
7881
  onStop: handleStop,
5655
7882
  agentOptions,
@@ -5896,13 +8123,14 @@ var SparkleIcon = () => /* @__PURE__ */ import_react16.default.createElement("sv
5896
8123
  var normalizeConversationListPayload = (payload) => {
5897
8124
  if (!payload) return [];
5898
8125
  const conversations = Array.isArray(payload) ? payload : payload.conversations || [];
5899
- return conversations.map((conv) => {
8126
+ const normalized = conversations.map((conv, index) => {
5900
8127
  let title = conv.title && conv.title !== "Conversation" ? conv.title : "";
5901
8128
  if (!title) {
5902
8129
  title = conv.summary || extractTitleFromConversation(conv);
5903
8130
  }
8131
+ const resolvedConversationId = conv.conversationId || conv.id || conv.conversation_id || `conversation-${index}-${Date.now()}`;
5904
8132
  return {
5905
- conversationId: conv.conversationId || conv.id || conv.conversation_id,
8133
+ conversationId: resolvedConversationId,
5906
8134
  title,
5907
8135
  summary: conv.summary,
5908
8136
  createdAt: conv.createdAt || conv.created_at || conv.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
@@ -5911,6 +8139,20 @@ var normalizeConversationListPayload = (payload) => {
5911
8139
  messageCount: conv.messageCount || conv.message_count
5912
8140
  };
5913
8141
  });
8142
+ const dedupedById = /* @__PURE__ */ new Map();
8143
+ for (const conv of normalized) {
8144
+ const existing = dedupedById.get(conv.conversationId);
8145
+ if (!existing) {
8146
+ dedupedById.set(conv.conversationId, conv);
8147
+ continue;
8148
+ }
8149
+ const existingUpdated = new Date(existing.updatedAt).getTime();
8150
+ const incomingUpdated = new Date(conv.updatedAt).getTime();
8151
+ if (incomingUpdated >= existingUpdated) {
8152
+ dedupedById.set(conv.conversationId, conv);
8153
+ }
8154
+ }
8155
+ return Array.from(dedupedById.values());
5914
8156
  };
5915
8157
  var extractTitleFromConversation = (conv) => {
5916
8158
  var _a;
@@ -5984,6 +8226,248 @@ var groupConversationsByTime = (conversations, showAllGroups = false) => {
5984
8226
  });
5985
8227
  return Object.entries(groups).filter(([_, convs]) => showAllGroups || convs.length > 0).map(([label, conversations2]) => ({ label, conversations: conversations2, count: conversations2.length }));
5986
8228
  };
8229
+ var toRecord = (value) => {
8230
+ if (typeof value === "object" && value !== null) {
8231
+ return value;
8232
+ }
8233
+ return null;
8234
+ };
8235
+ var toStringValue = (value) => {
8236
+ if (typeof value === "string") return value;
8237
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
8238
+ return "";
8239
+ };
8240
+ var normalizeContentText = (value) => {
8241
+ if (typeof value === "string") return value;
8242
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
8243
+ if (Array.isArray(value)) {
8244
+ return value.map((item) => normalizeContentText(item)).filter(Boolean).join("\n").trim();
8245
+ }
8246
+ const record = toRecord(value);
8247
+ if (!record) return "";
8248
+ const directText = toStringValue(record.text);
8249
+ if (directText) return directText;
8250
+ const directValue = toStringValue(record.value);
8251
+ if (directValue) return directValue;
8252
+ const directContent = toStringValue(record.content);
8253
+ if (directContent) return directContent;
8254
+ if (Array.isArray(record.content)) {
8255
+ return normalizeContentText(record.content);
8256
+ }
8257
+ if (Array.isArray(record.parts)) {
8258
+ return normalizeContentText(record.parts);
8259
+ }
8260
+ if (record.content && typeof record.content === "object") {
8261
+ return normalizeContentText(record.content);
8262
+ }
8263
+ return "";
8264
+ };
8265
+ var parseCallMessages = (rawMessages) => {
8266
+ var _a, _b;
8267
+ let parsed = rawMessages;
8268
+ if (typeof rawMessages === "string") {
8269
+ try {
8270
+ parsed = JSON.parse(rawMessages);
8271
+ } catch (e) {
8272
+ return [];
8273
+ }
8274
+ }
8275
+ const parsedRecord = toRecord(parsed);
8276
+ if (parsedRecord && Array.isArray(parsedRecord.messages)) {
8277
+ parsed = parsedRecord.messages;
8278
+ }
8279
+ if (!Array.isArray(parsed)) {
8280
+ return [];
8281
+ }
8282
+ const normalized = [];
8283
+ for (const message of parsed) {
8284
+ const messageRecord = toRecord(message);
8285
+ if (!messageRecord) continue;
8286
+ const role = toStringValue(messageRecord.role).toLowerCase().trim();
8287
+ if (!role) continue;
8288
+ const content = normalizeContentText(
8289
+ (_b = (_a = messageRecord.content) != null ? _a : messageRecord.text) != null ? _b : messageRecord.message
8290
+ ).trim();
8291
+ if (!content) continue;
8292
+ normalized.push({ role, content });
8293
+ }
8294
+ return normalized;
8295
+ };
8296
+ var shouldSkipTranscriptMessage = (message) => {
8297
+ return message.role === "system" || message.role === "user" && message.content.startsWith("__system__:");
8298
+ };
8299
+ var parseTimestampMs = (value) => {
8300
+ const timestamp = toStringValue(value).trim();
8301
+ if (!timestamp) return null;
8302
+ const parsed = Date.parse(timestamp);
8303
+ return Number.isFinite(parsed) ? parsed : null;
8304
+ };
8305
+ var getCallTimestampMs = (call, fallbackIndex) => {
8306
+ const timestampCandidates = [
8307
+ call.createdAt,
8308
+ call.created_at,
8309
+ call.timestamp,
8310
+ call.updatedAt,
8311
+ call.updated_at
8312
+ ];
8313
+ for (const candidate of timestampCandidates) {
8314
+ const parsed = parseTimestampMs(candidate);
8315
+ if (parsed !== null) return parsed;
8316
+ }
8317
+ return fallbackIndex;
8318
+ };
8319
+ var getCallId = (call) => {
8320
+ const id = toStringValue(call.id);
8321
+ if (id) return id;
8322
+ return toStringValue(call.callId);
8323
+ };
8324
+ var normalizeCallsPayload = (payload) => {
8325
+ const payloadRecord = toRecord(payload);
8326
+ const calls = Array.isArray(payload) ? payload : payloadRecord && Array.isArray(payloadRecord.calls) ? payloadRecord.calls : [];
8327
+ return calls.map((call) => toRecord(call)).filter((call) => call !== null);
8328
+ };
8329
+ var turnsEqual = (left, right) => {
8330
+ return left.prompt === right.prompt && left.response === right.response;
8331
+ };
8332
+ var getPrefixMatchLength = (existing, incoming) => {
8333
+ const max = Math.min(existing.length, incoming.length);
8334
+ let matched = 0;
8335
+ while (matched < max && turnsEqual(existing[matched], incoming[matched])) {
8336
+ matched += 1;
8337
+ }
8338
+ return matched;
8339
+ };
8340
+ var getSuffixPrefixOverlap = (existing, incoming) => {
8341
+ const max = Math.min(existing.length, incoming.length);
8342
+ for (let overlap = max; overlap > 0; overlap -= 1) {
8343
+ let matches = true;
8344
+ for (let idx = 0; idx < overlap; idx += 1) {
8345
+ const left = existing[existing.length - overlap + idx];
8346
+ const right = incoming[idx];
8347
+ if (!turnsEqual(left, right)) {
8348
+ matches = false;
8349
+ break;
8350
+ }
8351
+ }
8352
+ if (matches) return overlap;
8353
+ }
8354
+ return 0;
8355
+ };
8356
+ var mergeTranscriptTurns = (existing, incoming) => {
8357
+ if (incoming.length === 0) return existing;
8358
+ if (existing.length === 0) return [...incoming];
8359
+ const prefixMatchLength = getPrefixMatchLength(existing, incoming);
8360
+ if (prefixMatchLength > 0) {
8361
+ return [...existing, ...incoming.slice(prefixMatchLength)];
8362
+ }
8363
+ const overlap = getSuffixPrefixOverlap(existing, incoming);
8364
+ if (overlap > 0) {
8365
+ return [...existing, ...incoming.slice(overlap)];
8366
+ }
8367
+ return [...existing, ...incoming];
8368
+ };
8369
+ var buildTurnsFromMessages = (messages, fallbackResponse, callId, timestampMs) => {
8370
+ const turns = [];
8371
+ for (let index = 0; index < messages.length; index += 1) {
8372
+ const message = messages[index];
8373
+ if (!message || message.role !== "user") continue;
8374
+ const prompt = message.content.trim();
8375
+ if (!prompt) continue;
8376
+ let response = "";
8377
+ let reachedNextUser = false;
8378
+ for (let lookAhead = index + 1; lookAhead < messages.length; lookAhead += 1) {
8379
+ const nextMessage = messages[lookAhead];
8380
+ if (!nextMessage) continue;
8381
+ if (nextMessage.role === "assistant") {
8382
+ response = nextMessage.content.trim();
8383
+ index = lookAhead;
8384
+ break;
8385
+ }
8386
+ if (nextMessage.role === "user") {
8387
+ reachedNextUser = true;
8388
+ break;
8389
+ }
8390
+ }
8391
+ if (!response && !reachedNextUser) {
8392
+ response = fallbackResponse;
8393
+ }
8394
+ if (!response) continue;
8395
+ turns.push({
8396
+ prompt,
8397
+ response,
8398
+ callId,
8399
+ timestampMs
8400
+ });
8401
+ }
8402
+ return turns;
8403
+ };
8404
+ var buildTranscriptTurnsFromCalls = (calls) => {
8405
+ const orderedCalls = calls.map((call, index) => ({
8406
+ call,
8407
+ index,
8408
+ timestampMs: getCallTimestampMs(call, index)
8409
+ })).sort((left, right) => {
8410
+ if (left.timestampMs === right.timestampMs) return left.index - right.index;
8411
+ return left.timestampMs - right.timestampMs;
8412
+ });
8413
+ let mergedTurns = [];
8414
+ for (const orderedCall of orderedCalls) {
8415
+ const { call, timestampMs } = orderedCall;
8416
+ const callId = getCallId(call);
8417
+ const fallbackResponse = normalizeContentText(call.response).trim();
8418
+ const parsedMessages = parseCallMessages(call.messages).filter(
8419
+ (message) => !shouldSkipTranscriptMessage(message)
8420
+ );
8421
+ let callTurns = buildTurnsFromMessages(
8422
+ parsedMessages,
8423
+ fallbackResponse,
8424
+ callId,
8425
+ timestampMs
8426
+ );
8427
+ if (callTurns.length === 0) {
8428
+ const fallbackPrompt = normalizeContentText(call.prompt).trim();
8429
+ if (fallbackPrompt && fallbackResponse) {
8430
+ callTurns = [
8431
+ {
8432
+ prompt: fallbackPrompt,
8433
+ response: fallbackResponse,
8434
+ callId,
8435
+ timestampMs
8436
+ }
8437
+ ];
8438
+ }
8439
+ }
8440
+ mergedTurns = mergeTranscriptTurns(mergedTurns, callTurns);
8441
+ }
8442
+ return mergedTurns;
8443
+ };
8444
+ var buildHistoryFromTranscriptTurns = (turns) => {
8445
+ const history = {};
8446
+ const keyUsageByPrompt = /* @__PURE__ */ new Map();
8447
+ turns.forEach((turn, index) => {
8448
+ var _a;
8449
+ const prompt = turn.prompt.trim();
8450
+ const response = turn.response.trim();
8451
+ if (!prompt || !response) return;
8452
+ const baseTimestampMs = Number.isFinite(turn.timestampMs) ? turn.timestampMs : index;
8453
+ const keySeed = `${baseTimestampMs}:${prompt}`;
8454
+ const keyUsageCount = (_a = keyUsageByPrompt.get(keySeed)) != null ? _a : 0;
8455
+ keyUsageByPrompt.set(keySeed, keyUsageCount + 1);
8456
+ const uniqueTimestamp = new Date(baseTimestampMs + keyUsageCount).toISOString();
8457
+ const historyKey = `${uniqueTimestamp}:${prompt}`;
8458
+ history[historyKey] = {
8459
+ content: response,
8460
+ callId: turn.callId
8461
+ };
8462
+ });
8463
+ return history;
8464
+ };
8465
+ var truncatePromptForTitle = (prompt) => {
8466
+ if (prompt.length > 60) {
8467
+ return `${prompt.slice(0, 57)}...`;
8468
+ }
8469
+ return prompt;
8470
+ };
5987
8471
  var EMPTY_ARRAY = [];
5988
8472
  var EMPTY_HISTORY = {};
5989
8473
  var ChatPanelWrapper = ({
@@ -6017,6 +8501,7 @@ var ChatPanelWrapper = ({
6017
8501
  disabledSectionIds,
6018
8502
  onToggleSection,
6019
8503
  onConversationCreated,
8504
+ onBeforeSend,
6020
8505
  conversationInitialPrompt,
6021
8506
  // New props from ChatPanel port
6022
8507
  cssUrl,
@@ -6033,7 +8518,11 @@ var ChatPanelWrapper = ({
6033
8518
  callToActionEmailAddress,
6034
8519
  callToActionEmailSubject,
6035
8520
  customerEmailCaptureMode,
6036
- customerEmailCapturePlaceholder
8521
+ customerEmailCapturePlaceholder,
8522
+ resolveMcpAuthHeaders,
8523
+ localToolExecutors,
8524
+ traceContextMode,
8525
+ autoApproveTools
6037
8526
  }) => {
6038
8527
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
6039
8528
  const convAgentProfile = getAgent(activeConv.agentId);
@@ -6057,6 +8546,16 @@ var ChatPanelWrapper = ({
6057
8546
  },
6058
8547
  [onConversationCreated, activeConv.conversationId]
6059
8548
  );
8549
+ const beforeSendCallback = (0, import_react16.useCallback)(
8550
+ (payload) => {
8551
+ if (!onBeforeSend) return;
8552
+ return onBeforeSend(__spreadProps(__spreadValues({}, payload), {
8553
+ conversationId: payload.conversationId || activeConv.conversationId,
8554
+ agentId: payload.agentId || activeConv.agentId
8555
+ }));
8556
+ },
8557
+ [onBeforeSend, activeConv.conversationId, activeConv.agentId]
8558
+ );
6060
8559
  const agentStatus = convAgentProfile == null ? void 0 : convAgentProfile.status;
6061
8560
  const promptsString = (convAgentMetadata == null ? void 0 : convAgentMetadata.displayFollowOnPrompts) || "";
6062
8561
  let effectiveFollowOnQuestions;
@@ -6120,6 +8619,7 @@ var ChatPanelWrapper = ({
6120
8619
  disabledSectionIds,
6121
8620
  onToggleSection,
6122
8621
  onConversationCreated: conversationCreatedCallback,
8622
+ onBeforeSend: beforeSendCallback,
6123
8623
  cssUrl,
6124
8624
  markdownClass,
6125
8625
  width,
@@ -6134,7 +8634,11 @@ var ChatPanelWrapper = ({
6134
8634
  callToActionEmailAddress: (_g = callToActionEmailAddress != null ? callToActionEmailAddress : convAgentMetadata.displayCallToActionEmailAddress) != null ? _g : "",
6135
8635
  callToActionEmailSubject: (_h = callToActionEmailSubject != null ? callToActionEmailSubject : convAgentMetadata.displayCallToActionEmailSubject) != null ? _h : "Agent CTA submitted",
6136
8636
  customerEmailCaptureMode: (_i = customerEmailCaptureMode != null ? customerEmailCaptureMode : convAgentMetadata == null ? void 0 : convAgentMetadata.customerEmailCaptureMode) != null ? _i : "HIDE",
6137
- customerEmailCapturePlaceholder: (_j = customerEmailCapturePlaceholder != null ? customerEmailCapturePlaceholder : convAgentMetadata == null ? void 0 : convAgentMetadata.customerEmailCapturePlaceholder) != null ? _j : "Please enter your email..."
8637
+ customerEmailCapturePlaceholder: (_j = customerEmailCapturePlaceholder != null ? customerEmailCapturePlaceholder : convAgentMetadata == null ? void 0 : convAgentMetadata.customerEmailCapturePlaceholder) != null ? _j : "Please enter your email...",
8638
+ resolveMcpAuthHeaders,
8639
+ localToolExecutors,
8640
+ traceContextMode,
8641
+ autoApproveTools
6138
8642
  }
6139
8643
  )
6140
8644
  );
@@ -6185,6 +8689,7 @@ var AIAgentPanel = import_react16.default.forwardRef(({
6185
8689
  enableContextDetailView = false,
6186
8690
  onAgentSwitch,
6187
8691
  onConversationChange,
8692
+ onBeforeSend,
6188
8693
  historyChangedCallback,
6189
8694
  responseCompleteCallback,
6190
8695
  thumbsUpClick,
@@ -6216,7 +8721,11 @@ var AIAgentPanel = import_react16.default.forwardRef(({
6216
8721
  callToActionEmailAddress,
6217
8722
  callToActionEmailSubject,
6218
8723
  customerEmailCaptureMode,
6219
- customerEmailCapturePlaceholder
8724
+ customerEmailCapturePlaceholder,
8725
+ resolveMcpAuthHeaders,
8726
+ localToolExecutors,
8727
+ traceContextMode = "standard",
8728
+ autoApproveTools
6220
8729
  }, ref) => {
6221
8730
  var _a, _b, _c, _d;
6222
8731
  (0, import_react16.useEffect)(() => {
@@ -6236,13 +8745,7 @@ var AIAgentPanel = import_react16.default.forwardRef(({
6236
8745
  const [uncontrolledIsCollapsed, setUncontrolledIsCollapsed] = (0, import_react16.useState)(collapsible && defaultCollapsed);
6237
8746
  const isControlled = controlledIsCollapsed !== void 0;
6238
8747
  const isCollapsed = isControlled ? controlledIsCollapsed : uncontrolledIsCollapsed;
6239
- const [isHistoryCollapsed, setIsHistoryCollapsed] = (0, import_react16.useState)(() => {
6240
- if (typeof window !== "undefined") {
6241
- const saved = localStorage.getItem("ai-agent-panel-history-collapsed");
6242
- return saved === "true";
6243
- }
6244
- return false;
6245
- });
8748
+ const [isHistoryCollapsed, setIsHistoryCollapsed] = (0, import_react16.useState)(true);
6246
8749
  const [panelWidth, setPanelWidth] = (0, import_react16.useState)(() => {
6247
8750
  if (typeof window !== "undefined") {
6248
8751
  const savedWidth = localStorage.getItem("ai-agent-panel-width");
@@ -6344,8 +8847,12 @@ var AIAgentPanel = import_react16.default.forwardRef(({
6344
8847
  const [conversationsLoading, setConversationsLoading] = (0, import_react16.useState)(false);
6345
8848
  const [conversationsError, setConversationsError] = (0, import_react16.useState)(null);
6346
8849
  const [searchQuery, setSearchQuery] = (0, import_react16.useState)("");
8850
+ const controlledConversationId = typeof conversation === "string" && conversation.trim() !== "" ? conversation.trim() : null;
8851
+ const isConversationControlled = controlledConversationId !== null;
6347
8852
  const [activeConversations, setActiveConversations] = (0, import_react16.useState)(/* @__PURE__ */ new Map());
6348
- const [currentConversationId, setCurrentConversationId] = (0, import_react16.useState)(conversation || null);
8853
+ const [currentConversationId, setCurrentConversationId] = (0, import_react16.useState)(
8854
+ controlledConversationId
8855
+ );
6349
8856
  const [conversationFirstPrompts, setConversationFirstPrompts] = (0, import_react16.useState)({});
6350
8857
  const [loadingPrompts, setLoadingPrompts] = (0, import_react16.useState)(/* @__PURE__ */ new Set());
6351
8858
  const [loadingConversationId, setLoadingConversationId] = (0, import_react16.useState)(null);
@@ -6365,6 +8872,7 @@ var AIAgentPanel = import_react16.default.forwardRef(({
6365
8872
  agentList
6366
8873
  } = useAgentRegistry(agentIds, { url, localOverrides });
6367
8874
  const fetchInProgressRef = (0, import_react16.useRef)(false);
8875
+ const loadingTranscriptIdsRef = (0, import_react16.useRef)(/* @__PURE__ */ new Set());
6368
8876
  const lastFetchedAgentRef = (0, import_react16.useRef)(null);
6369
8877
  const checkedPromptsRef = (0, import_react16.useRef)(/* @__PURE__ */ new Set());
6370
8878
  const fetchingPromptsRef = (0, import_react16.useRef)(/* @__PURE__ */ new Set());
@@ -6373,29 +8881,49 @@ var AIAgentPanel = import_react16.default.forwardRef(({
6373
8881
  activeConversationsRef.current = activeConversations;
6374
8882
  const currentConversationIdRef = (0, import_react16.useRef)(currentConversationId);
6375
8883
  currentConversationIdRef.current = currentConversationId;
6376
- import_react16.default.useImperativeHandle(ref, () => ({
6377
- startNewConversation: (prompt, agent) => {
6378
- const targetAgent = agent || currentAgentId;
8884
+ const commitConversationSelection = (0, import_react16.useCallback)(
8885
+ (conversationId, notifyChange) => {
8886
+ const shouldSyncLocalState = !isConversationControlled || !notifyChange || controlledConversationId === conversationId;
8887
+ if (shouldSyncLocalState) {
8888
+ setCurrentConversationId(conversationId);
8889
+ }
8890
+ if (notifyChange && conversationId && onConversationChange) {
8891
+ const shouldNotifyParent = !isConversationControlled || controlledConversationId !== conversationId;
8892
+ if (shouldNotifyParent) {
8893
+ onConversationChange(conversationId);
8894
+ }
8895
+ }
8896
+ },
8897
+ [controlledConversationId, isConversationControlled, onConversationChange]
8898
+ );
8899
+ const createDraftConversation = (0, import_react16.useCallback)(
8900
+ (agentId, conversationInitialPrompt) => {
6379
8901
  const tempId = `new-${Date.now()}`;
6380
8902
  setActiveConversations((prev) => {
6381
8903
  const next = new Map(prev);
6382
8904
  next.set(tempId, {
6383
8905
  conversationId: tempId,
6384
8906
  stableKey: tempId,
6385
- agentId: targetAgent,
8907
+ agentId,
6386
8908
  history: {},
8909
+ transcriptLoaded: true,
6387
8910
  isLoading: false,
6388
8911
  title: "New conversation",
6389
- conversationInitialPrompt: prompt
8912
+ conversationInitialPrompt
6390
8913
  });
6391
8914
  return next;
6392
8915
  });
6393
- setCurrentConversationId(tempId);
6394
- if (onConversationChange) {
6395
- onConversationChange(tempId);
6396
- }
8916
+ return tempId;
8917
+ },
8918
+ []
8919
+ );
8920
+ import_react16.default.useImperativeHandle(ref, () => ({
8921
+ startNewConversation: (prompt, agent) => {
8922
+ const targetAgent = agent || currentAgentId;
8923
+ const tempId = createDraftConversation(targetAgent, prompt);
8924
+ commitConversationSelection(tempId, true);
6397
8925
  }
6398
- }), [currentAgentId, onConversationChange]);
8926
+ }), [commitConversationSelection, createDraftConversation, currentAgentId]);
6399
8927
  const [showContextNotification, setShowContextNotification] = (0, import_react16.useState)(false);
6400
8928
  const prevContextRef = (0, import_react16.useRef)(null);
6401
8929
  const contextNotificationTimeoutRef = (0, import_react16.useRef)(null);
@@ -6537,14 +9065,15 @@ var AIAgentPanel = import_react16.default.forwardRef(({
6537
9065
  }
6538
9066
  }
6539
9067
  }), [apiKey, customerId, getAgent, fetchFirstPrompt]);
6540
- const loadConversationTranscript = (0, import_react16.useCallback)((conversationId, agentIdForConversation, title) => __async(void 0, null, function* () {
9068
+ const loadConversationTranscript = (0, import_react16.useCallback)((conversationId, agentIdForConversation, title, notifyConversationChange = true) => __async(void 0, null, function* () {
6541
9069
  var _a2;
6542
9070
  const existingActive = activeConversationsRef.current.get(conversationId);
6543
- if (existingActive) {
6544
- setCurrentConversationId(conversationId);
6545
- if (onConversationChange) {
6546
- onConversationChange(conversationId);
6547
- }
9071
+ if (existingActive && (existingActive.transcriptLoaded || Object.keys(existingActive.history || {}).length > 0)) {
9072
+ commitConversationSelection(conversationId, notifyConversationChange);
9073
+ return;
9074
+ }
9075
+ if (loadingTranscriptIdsRef.current.has(conversationId)) {
9076
+ commitConversationSelection(conversationId, notifyConversationChange);
6548
9077
  return;
6549
9078
  }
6550
9079
  const agentIdToUse = agentIdForConversation || currentAgentId;
@@ -6554,6 +9083,8 @@ var AIAgentPanel = import_react16.default.forwardRef(({
6554
9083
  setConversationsError("Missing API key or project ID");
6555
9084
  return;
6556
9085
  }
9086
+ loadingTranscriptIdsRef.current.add(conversationId);
9087
+ setConversationsError(null);
6557
9088
  setLoadingConversationId(conversationId);
6558
9089
  try {
6559
9090
  console.log("loadConversationTranscript - conversationId:", conversationId);
@@ -6566,57 +9097,34 @@ var AIAgentPanel = import_react16.default.forwardRef(({
6566
9097
  }
6567
9098
  });
6568
9099
  if (!response.ok) {
9100
+ if (response.status === 404) {
9101
+ const conversationTitle2 = title || conversationFirstPrompts[conversationId] || "New conversation";
9102
+ setActiveConversations((prev) => {
9103
+ const next = new Map(prev);
9104
+ next.set(conversationId, {
9105
+ conversationId,
9106
+ stableKey: conversationId,
9107
+ agentId: agentIdToUse,
9108
+ history: {},
9109
+ transcriptLoaded: true,
9110
+ isLoading: false,
9111
+ title: conversationTitle2
9112
+ });
9113
+ return next;
9114
+ });
9115
+ commitConversationSelection(conversationId, notifyConversationChange);
9116
+ return;
9117
+ }
6569
9118
  throw new Error(`Failed to load conversation (${response.status})`);
6570
9119
  }
6571
9120
  const payload = yield response.json();
6572
9121
  console.log("loadConversationTranscript - API response:", payload);
6573
- const history = {};
6574
- let firstPrompt = null;
6575
- if (Array.isArray(payload) && payload.length > 0) {
6576
- const lastCall = payload[payload.length - 1];
6577
- const callId = lastCall.id || "";
6578
- const timestamp = lastCall.createdAt || lastCall.created_at || (/* @__PURE__ */ new Date()).toISOString();
6579
- if (lastCall.messages) {
6580
- try {
6581
- const messages2 = JSON.parse(lastCall.messages);
6582
- console.log("loadConversationTranscript - parsed messages:", messages2);
6583
- const relevantMessages = messages2.filter(
6584
- (msg) => msg.role !== "system" && !(msg.role === "user" && msg.content.startsWith("__system__:"))
6585
- );
6586
- console.log("loadConversationTranscript - filtered messages:", relevantMessages);
6587
- for (let i = 0; i < relevantMessages.length; i++) {
6588
- const msg = relevantMessages[i];
6589
- if (!msg) continue;
6590
- if (msg.role === "user") {
6591
- if (!firstPrompt) {
6592
- firstPrompt = msg.content.length > 60 ? msg.content.slice(0, 57) + "..." : msg.content;
6593
- }
6594
- const nextMsg = relevantMessages[i + 1];
6595
- if (nextMsg && nextMsg.role === "assistant") {
6596
- const historyKey = `${timestamp}:${msg.content}`;
6597
- history[historyKey] = {
6598
- content: nextMsg.content,
6599
- callId
6600
- };
6601
- i++;
6602
- } else {
6603
- if (lastCall.response) {
6604
- const historyKey = `${timestamp}:${msg.content}`;
6605
- history[historyKey] = {
6606
- content: lastCall.response,
6607
- callId
6608
- };
6609
- }
6610
- }
6611
- }
6612
- }
6613
- } catch (err) {
6614
- console.error("loadConversationTranscript - failed to parse messages property:", err);
6615
- }
6616
- }
6617
- }
6618
- console.log("loadConversationTranscript - created", Object.keys(history).length, "history entries");
6619
- console.log("loadConversationTranscript - parsed history:", history);
9122
+ const calls = normalizeCallsPayload(payload);
9123
+ const transcriptTurns = buildTranscriptTurnsFromCalls(calls);
9124
+ const history = buildHistoryFromTranscriptTurns(transcriptTurns);
9125
+ const firstPrompt = transcriptTurns.length > 0 ? truncatePromptForTitle(transcriptTurns[0].prompt) : null;
9126
+ console.log("loadConversationTranscript - parsed calls:", calls.length);
9127
+ console.log("loadConversationTranscript - created history entries:", Object.keys(history).length);
6620
9128
  if (firstPrompt) {
6621
9129
  setConversationFirstPrompts((prev) => __spreadProps(__spreadValues({}, prev), {
6622
9130
  [conversationId]: firstPrompt
@@ -6631,22 +9139,85 @@ var AIAgentPanel = import_react16.default.forwardRef(({
6631
9139
  // Use real ID as stable key when loading from API
6632
9140
  agentId: agentIdToUse,
6633
9141
  history,
9142
+ transcriptLoaded: true,
6634
9143
  isLoading: false,
6635
9144
  title: conversationTitle
6636
9145
  });
6637
9146
  return next;
6638
9147
  });
6639
- setCurrentConversationId(conversationId);
6640
- if (onConversationChange) {
6641
- onConversationChange(conversationId);
6642
- }
6643
- setLoadingConversationId(null);
9148
+ commitConversationSelection(conversationId, notifyConversationChange);
6644
9149
  } catch (error) {
6645
9150
  console.error("Failed to load conversation:", error);
6646
9151
  setConversationsError(error.message || "Failed to load conversation");
6647
- setLoadingConversationId(null);
9152
+ } finally {
9153
+ loadingTranscriptIdsRef.current.delete(conversationId);
9154
+ setLoadingConversationId((prev) => prev === conversationId ? null : prev);
9155
+ }
9156
+ }), [apiKey, commitConversationSelection, conversationFirstPrompts, currentAgentId, getAgent]);
9157
+ (0, import_react16.useEffect)(() => {
9158
+ if (!isConversationControlled) return;
9159
+ const targetConversationId = controlledConversationId;
9160
+ if (!targetConversationId) {
9161
+ if (currentConversationIdRef.current !== null) {
9162
+ setCurrentConversationId(null);
9163
+ }
9164
+ return;
9165
+ }
9166
+ if (targetConversationId.startsWith("new-")) {
9167
+ setActiveConversations((prev) => {
9168
+ if (prev.has(targetConversationId)) {
9169
+ return prev;
9170
+ }
9171
+ const next = new Map(prev);
9172
+ next.set(targetConversationId, {
9173
+ conversationId: targetConversationId,
9174
+ stableKey: targetConversationId,
9175
+ agentId: currentAgentId,
9176
+ history: {},
9177
+ transcriptLoaded: true,
9178
+ isLoading: false,
9179
+ title: "New conversation"
9180
+ });
9181
+ return next;
9182
+ });
9183
+ if (currentConversationIdRef.current !== targetConversationId) {
9184
+ setCurrentConversationId(targetConversationId);
9185
+ }
9186
+ return;
9187
+ }
9188
+ if (loadingConversationId === targetConversationId) {
9189
+ return;
9190
+ }
9191
+ const existingActive = activeConversationsRef.current.get(targetConversationId);
9192
+ if (existingActive && existingActive.transcriptLoaded && currentConversationIdRef.current === targetConversationId) {
9193
+ return;
9194
+ }
9195
+ const apiConversation = apiConversations.find(
9196
+ (conv) => conv.conversationId === targetConversationId
9197
+ );
9198
+ const targetAgentId = (apiConversation == null ? void 0 : apiConversation.agentId) || (existingActive == null ? void 0 : existingActive.agentId) || currentAgentId;
9199
+ if (!targetAgentId) {
9200
+ return;
9201
+ }
9202
+ if (targetAgentId !== currentAgentId) {
9203
+ setCurrentAgentId(targetAgentId);
6648
9204
  }
6649
- }), [apiKey, currentAgentId, getAgent, onConversationChange]);
9205
+ const targetTitle = conversationFirstPrompts[targetConversationId] || (apiConversation == null ? void 0 : apiConversation.title) || (existingActive == null ? void 0 : existingActive.title);
9206
+ void loadConversationTranscript(
9207
+ targetConversationId,
9208
+ targetAgentId,
9209
+ targetTitle,
9210
+ false
9211
+ );
9212
+ }, [
9213
+ apiConversations,
9214
+ controlledConversationId,
9215
+ conversationFirstPrompts,
9216
+ currentAgentId,
9217
+ isConversationControlled,
9218
+ loadConversationTranscript,
9219
+ loadingConversationId
9220
+ ]);
6650
9221
  const handleRefreshConversations = (0, import_react16.useCallback)(() => {
6651
9222
  fetchConversations(currentAgentId);
6652
9223
  }, [currentAgentId, fetchConversations]);
@@ -6663,30 +9234,18 @@ var AIAgentPanel = import_react16.default.forwardRef(({
6663
9234
  }
6664
9235
  }, [agentsLoading, currentAgentId, apiKey, fetchConversations, getAgent, showConversationHistory]);
6665
9236
  const handleNewConversation = (0, import_react16.useCallback)(() => {
6666
- const tempId = `new-${Date.now()}`;
6667
- setActiveConversations((prev) => {
6668
- const next = new Map(prev);
6669
- next.set(tempId, {
6670
- conversationId: tempId,
6671
- stableKey: tempId,
6672
- // Stable key never changes even when conversationId updates
6673
- agentId: currentAgentId,
6674
- history: {},
6675
- isLoading: false,
6676
- title: "New conversation"
6677
- });
6678
- return next;
6679
- });
6680
- setCurrentConversationId(tempId);
6681
- }, [currentAgentId]);
9237
+ const tempId = createDraftConversation(currentAgentId);
9238
+ commitConversationSelection(tempId, true);
9239
+ }, [commitConversationSelection, createDraftConversation, currentAgentId]);
6682
9240
  (0, import_react16.useEffect)(() => {
6683
9241
  var _a2;
9242
+ if (isConversationControlled) return;
6684
9243
  const agentProfile = getAgent(currentAgentId);
6685
9244
  const isAgentReady = (_a2 = agentProfile == null ? void 0 : agentProfile.metadata) == null ? void 0 : _a2.projectId;
6686
9245
  if (isAgentReady && !agentsLoading && activeConversations.size === 0) {
6687
9246
  handleNewConversation();
6688
9247
  }
6689
- }, [currentAgentId, agentsLoading, activeConversations.size, getAgent, handleNewConversation]);
9248
+ }, [currentAgentId, agentsLoading, activeConversations.size, getAgent, handleNewConversation, isConversationControlled]);
6690
9249
  const handleCloseConversation = (0, import_react16.useCallback)((conversationId, e) => {
6691
9250
  var _a2;
6692
9251
  if (e) {
@@ -6700,9 +9259,13 @@ var AIAgentPanel = import_react16.default.forwardRef(({
6700
9259
  if (currentConversationIdRef.current === conversationId) {
6701
9260
  const remaining = Array.from(activeConversationsRef.current.keys()).filter((id) => id !== conversationId);
6702
9261
  const nextId = remaining.length > 0 ? (_a2 = remaining[0]) != null ? _a2 : null : null;
6703
- setCurrentConversationId(nextId);
9262
+ if (nextId) {
9263
+ commitConversationSelection(nextId, true);
9264
+ } else if (!isConversationControlled) {
9265
+ setCurrentConversationId(null);
9266
+ }
6704
9267
  }
6705
- }, []);
9268
+ }, [commitConversationSelection, isConversationControlled]);
6706
9269
  const handleSelectConversation = (0, import_react16.useCallback)((conversationId) => {
6707
9270
  loadConversationTranscript(conversationId);
6708
9271
  }, [loadConversationTranscript]);
@@ -7003,6 +9566,7 @@ var AIAgentPanel = import_react16.default.forwardRef(({
7003
9566
  }
7004
9567
  next.set(targetConversationId, __spreadProps(__spreadValues({}, existing), {
7005
9568
  history,
9569
+ transcriptLoaded: true,
7006
9570
  title
7007
9571
  }));
7008
9572
  return next;
@@ -7092,26 +9656,23 @@ var AIAgentPanel = import_react16.default.forwardRef(({
7092
9656
  return prev;
7093
9657
  });
7094
9658
  if (currentConversationIdRef.current === tempId) {
7095
- setCurrentConversationId(realId);
7096
- if (onConversationChange) {
7097
- onConversationChange(realId);
7098
- }
9659
+ commitConversationSelection(realId, true);
7099
9660
  }
7100
- }, [onConversationChange]);
9661
+ }, [commitConversationSelection]);
7101
9662
  const toggleCollapse = (0, import_react16.useCallback)(() => {
7102
9663
  if (!collapsible) return;
7103
9664
  const newValue = !isCollapsed;
9665
+ if (!newValue) {
9666
+ setIsHistoryCollapsed(true);
9667
+ setShowSearch(false);
9668
+ }
7104
9669
  if (!isControlled) {
7105
9670
  setUncontrolledIsCollapsed(newValue);
7106
9671
  }
7107
9672
  onCollapsedChange == null ? void 0 : onCollapsedChange(newValue);
7108
9673
  }, [collapsible, isCollapsed, isControlled, onCollapsedChange]);
7109
9674
  const toggleHistoryCollapse = (0, import_react16.useCallback)(() => {
7110
- setIsHistoryCollapsed((prev) => {
7111
- const next = !prev;
7112
- localStorage.setItem("ai-agent-panel-history-collapsed", String(next));
7113
- return next;
7114
- });
9675
+ setIsHistoryCollapsed((prev) => !prev);
7115
9676
  }, []);
7116
9677
  const panelClasses = [
7117
9678
  "ai-agent-panel",
@@ -7183,27 +9744,11 @@ var AIAgentPanel = import_react16.default.forwardRef(({
7183
9744
  }
7184
9745
  onCollapsedChange == null ? void 0 : onCollapsedChange(false);
7185
9746
  if (hasActiveConversation && activeConvForAgent) {
7186
- setCurrentConversationId(activeConvForAgent.conversationId);
7187
9747
  setCurrentAgentId(agent.id);
7188
- if (onConversationChange) {
7189
- onConversationChange(activeConvForAgent.conversationId);
7190
- }
9748
+ commitConversationSelection(activeConvForAgent.conversationId, true);
7191
9749
  } else {
7192
- const tempId = `new-${Date.now()}`;
7193
- setActiveConversations((prev) => {
7194
- const next = new Map(prev);
7195
- next.set(tempId, {
7196
- conversationId: tempId,
7197
- stableKey: tempId,
7198
- // Stable key never changes
7199
- agentId: agent.id,
7200
- history: {},
7201
- isLoading: false,
7202
- title: "New conversation"
7203
- });
7204
- return next;
7205
- });
7206
- setCurrentConversationId(tempId);
9750
+ const tempId = createDraftConversation(agent.id);
9751
+ commitConversationSelection(tempId, true);
7207
9752
  setCurrentAgentId(agent.id);
7208
9753
  }
7209
9754
  },
@@ -7288,27 +9833,11 @@ var AIAgentPanel = import_react16.default.forwardRef(({
7288
9833
  title: agent.name,
7289
9834
  onClick: () => {
7290
9835
  if (hasActiveConversation && activeConvForAgent) {
7291
- setCurrentConversationId(activeConvForAgent.conversationId);
7292
9836
  setCurrentAgentId(agent.id);
7293
- if (onConversationChange) {
7294
- onConversationChange(activeConvForAgent.conversationId);
7295
- }
9837
+ commitConversationSelection(activeConvForAgent.conversationId, true);
7296
9838
  } else {
7297
- const tempId = `new-${Date.now()}`;
7298
- setActiveConversations((prev) => {
7299
- const next = new Map(prev);
7300
- next.set(tempId, {
7301
- conversationId: tempId,
7302
- stableKey: tempId,
7303
- // Stable key never changes
7304
- agentId: agent.id,
7305
- history: {},
7306
- isLoading: false,
7307
- title: "New conversation"
7308
- });
7309
- return next;
7310
- });
7311
- setCurrentConversationId(tempId);
9839
+ const tempId = createDraftConversation(agent.id);
9840
+ commitConversationSelection(tempId, true);
7312
9841
  setCurrentAgentId(agent.id);
7313
9842
  }
7314
9843
  },
@@ -7361,10 +9890,7 @@ var AIAgentPanel = import_react16.default.forwardRef(({
7361
9890
  key: activeConv.stableKey,
7362
9891
  className: `ai-agent-panel__conversation ai-agent-panel__conversation--active-item ${currentConversationId === activeConv.conversationId ? "ai-agent-panel__conversation--current" : ""}`,
7363
9892
  onClick: () => {
7364
- setCurrentConversationId(activeConv.conversationId);
7365
- if (onConversationChange) {
7366
- onConversationChange(activeConv.conversationId);
7367
- }
9893
+ commitConversationSelection(activeConv.conversationId, true);
7368
9894
  }
7369
9895
  },
7370
9896
  /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__conversation-content" }, /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__conversation-title" }, activeConv.isLoading && /* @__PURE__ */ import_react16.default.createElement(LoadingDotIcon, null), /* @__PURE__ */ import_react16.default.createElement("span", null, activeConv.title))),
@@ -7385,12 +9911,12 @@ var AIAgentPanel = import_react16.default.forwardRef(({
7385
9911
  },
7386
9912
  /* @__PURE__ */ import_react16.default.createElement("span", null, group.label, " ", group.count > 0 && `(${group.count})`),
7387
9913
  /* @__PURE__ */ import_react16.default.createElement("span", { className: "ai-agent-panel__group-chevron" }, expandedSections[group.label] ? "\u25BC" : "\u25B6")
7388
- ), expandedSections[group.label] && group.conversations.length > 0 && group.conversations.map((conv) => {
9914
+ ), expandedSections[group.label] && group.conversations.length > 0 && group.conversations.map((conv, convIndex) => {
7389
9915
  const isActive = activeConversations.has(conv.conversationId);
7390
9916
  return /* @__PURE__ */ import_react16.default.createElement(
7391
9917
  "div",
7392
9918
  {
7393
- key: conv.conversationId,
9919
+ key: `${group.label}-${conv.conversationId}-${convIndex}`,
7394
9920
  className: `ai-agent-panel__conversation ${currentConversationId === conv.conversationId ? "ai-agent-panel__conversation--current" : ""} ${isActive ? "ai-agent-panel__conversation--in-active" : ""}`,
7395
9921
  onClick: () => handleConversationSelect(conv)
7396
9922
  },
@@ -7400,7 +9926,7 @@ var AIAgentPanel = import_react16.default.forwardRef(({
7400
9926
  /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__chat" }, !showConversationHistory && collapsible && /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__chat-header" }, /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__chat-header-spacer" }), /* @__PURE__ */ import_react16.default.createElement(Tooltip, { content: "Collapse Panel", side: position === "right" ? "left" : "right" }, /* @__PURE__ */ import_react16.default.createElement(Button, { variant: "ghost", size: "icon", onClick: toggleCollapse }, position === "right" ? /* @__PURE__ */ import_react16.default.createElement(ChevronRightIcon, null) : /* @__PURE__ */ import_react16.default.createElement(ChevronLeftIcon, null)))), showContextNotification && /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__context-notification" }, /* @__PURE__ */ import_react16.default.createElement(SparkleIcon, null), /* @__PURE__ */ import_react16.default.createElement("span", null, "Agent now has new context")), activeConversationsList.map((activeConv) => /* @__PURE__ */ import_react16.default.createElement(
7401
9927
  ChatPanelWrapper,
7402
9928
  {
7403
- key: `${activeConv.stableKey}-${activeConv.agentId}`,
9929
+ key: activeConv.stableKey,
7404
9930
  activeConv,
7405
9931
  currentConversationId,
7406
9932
  getAgent,
@@ -7431,6 +9957,7 @@ var AIAgentPanel = import_react16.default.forwardRef(({
7431
9957
  disabledSectionIds: currentDisabledSections,
7432
9958
  onToggleSection: handleContextSectionToggle,
7433
9959
  onConversationCreated: handleConversationCreated,
9960
+ onBeforeSend,
7434
9961
  conversationInitialPrompt: activeConv.conversationInitialPrompt,
7435
9962
  cssUrl,
7436
9963
  markdownClass,
@@ -7446,7 +9973,11 @@ var AIAgentPanel = import_react16.default.forwardRef(({
7446
9973
  callToActionEmailAddress,
7447
9974
  callToActionEmailSubject,
7448
9975
  customerEmailCaptureMode,
7449
- customerEmailCapturePlaceholder
9976
+ customerEmailCapturePlaceholder,
9977
+ resolveMcpAuthHeaders,
9978
+ localToolExecutors,
9979
+ traceContextMode,
9980
+ autoApproveTools
7450
9981
  }
7451
9982
  )), loadingConversationId && /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__conversation-loading-overlay" }, /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__loading-spinner" }), /* @__PURE__ */ import_react16.default.createElement("p", null, "Loading conversation...")), currentAgentMetadata && activeConversationsList.length === 0 && !loadingConversationId && /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__empty-chat" }, /* @__PURE__ */ import_react16.default.createElement(MessageIcon, null), /* @__PURE__ */ import_react16.default.createElement("p", null, "Select a conversation or start a new one"), /* @__PURE__ */ import_react16.default.createElement(Button, { variant: "default", size: "sm", onClick: handleNewConversation }, "New Conversation")), agentsLoading && !currentAgentMetadata && /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__loading" }, /* @__PURE__ */ import_react16.default.createElement("div", { className: "ai-agent-panel__loading-spinner" }), /* @__PURE__ */ import_react16.default.createElement("p", null, "Loading agent..."))),
7452
9983
  /* @__PURE__ */ import_react16.default.createElement(