@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.mjs CHANGED
@@ -652,6 +652,302 @@ var ThinkingBlock = ({
652
652
  );
653
653
  };
654
654
 
655
+ // src/mcpAuth.ts
656
+ function normalizeMcpHeaders(value) {
657
+ const normalized = {};
658
+ if (!value) return normalized;
659
+ for (const [key, raw] of Object.entries(value)) {
660
+ if (typeof raw !== "string") continue;
661
+ const trimmedValue = raw.trim();
662
+ if (!trimmedValue) continue;
663
+ normalized[key] = trimmedValue;
664
+ }
665
+ return normalized;
666
+ }
667
+
668
+ // src/toolArgsParser.ts
669
+ var TOOL_ARGS_CANDIDATE_PARSE_DEPTH = 3;
670
+ var isPlainObject = (value) => {
671
+ return typeof value === "object" && value !== null && !Array.isArray(value);
672
+ };
673
+ var stripMarkdownCodeFence = (input) => {
674
+ const trimmed = input.trim();
675
+ if (!trimmed.startsWith("```")) return trimmed;
676
+ const withoutStart = trimmed.replace(/^```[a-zA-Z0-9_-]*\s*/u, "");
677
+ return withoutStart.replace(/```$/u, "").trim();
678
+ };
679
+ var stripOuterQuotes = (input) => {
680
+ const trimmed = input.trim();
681
+ if (trimmed.length < 2) return trimmed;
682
+ const first = trimmed[0];
683
+ const last = trimmed[trimmed.length - 1];
684
+ if (first === '"' && last === '"' || first === "'" && last === "'") {
685
+ return trimmed.slice(1, -1).trim();
686
+ }
687
+ return trimmed;
688
+ };
689
+ var normalizeSmartQuotes = (input) => {
690
+ return input.replace(/[“”]/gu, '"').replace(/[‘’]/gu, "'");
691
+ };
692
+ var convertSingleQuotedStrings = (input) => {
693
+ let output = "";
694
+ let inSingle = false;
695
+ let inDouble = false;
696
+ let escaped = false;
697
+ for (let index = 0; index < input.length; index += 1) {
698
+ const char = input[index];
699
+ if (inSingle) {
700
+ if (escaped) {
701
+ output += char === '"' ? '\\"' : char;
702
+ escaped = false;
703
+ continue;
704
+ }
705
+ if (char === "\\") {
706
+ output += "\\";
707
+ escaped = true;
708
+ continue;
709
+ }
710
+ if (char === "'") {
711
+ output += '"';
712
+ inSingle = false;
713
+ continue;
714
+ }
715
+ output += char === '"' ? '\\"' : char;
716
+ continue;
717
+ }
718
+ if (inDouble) {
719
+ output += char;
720
+ if (escaped) {
721
+ escaped = false;
722
+ continue;
723
+ }
724
+ if (char === "\\") {
725
+ escaped = true;
726
+ continue;
727
+ }
728
+ if (char === '"') {
729
+ inDouble = false;
730
+ }
731
+ continue;
732
+ }
733
+ if (char === "'") {
734
+ output += '"';
735
+ inSingle = true;
736
+ continue;
737
+ }
738
+ if (char === '"') {
739
+ output += '"';
740
+ inDouble = true;
741
+ continue;
742
+ }
743
+ output += char;
744
+ }
745
+ return output;
746
+ };
747
+ var quoteBareObjectKeys = (input) => {
748
+ return input.replace(
749
+ /([{,]\s*)([A-Za-z_$][A-Za-z0-9_$-]*)(\s*:)/gu,
750
+ '$1"$2"$3'
751
+ );
752
+ };
753
+ var stripTrailingCommas = (input) => {
754
+ return input.replace(/,\s*([}\]])/gu, "$1");
755
+ };
756
+ var normalizeEscapesOutsideStrings = (input) => {
757
+ let output = "";
758
+ let inDouble = false;
759
+ let inSingle = false;
760
+ let escaped = false;
761
+ for (let index = 0; index < input.length; index += 1) {
762
+ const char = input[index];
763
+ const next = index + 1 < input.length ? input[index + 1] : "";
764
+ if (inDouble || inSingle) {
765
+ output += char;
766
+ if (escaped) {
767
+ escaped = false;
768
+ continue;
769
+ }
770
+ if (char === "\\") {
771
+ escaped = true;
772
+ continue;
773
+ }
774
+ if (inDouble && char === '"') {
775
+ inDouble = false;
776
+ } else if (inSingle && char === "'") {
777
+ inSingle = false;
778
+ }
779
+ continue;
780
+ }
781
+ if (char === '"') {
782
+ inDouble = true;
783
+ output += char;
784
+ continue;
785
+ }
786
+ if (char === "'") {
787
+ inSingle = true;
788
+ output += char;
789
+ continue;
790
+ }
791
+ if (char === "\\") {
792
+ if (next === "n") {
793
+ output += "\n";
794
+ index += 1;
795
+ continue;
796
+ }
797
+ if (next === "r") {
798
+ output += "\r";
799
+ index += 1;
800
+ continue;
801
+ }
802
+ if (next === "t") {
803
+ output += " ";
804
+ index += 1;
805
+ continue;
806
+ }
807
+ if (next === '"') {
808
+ output += '"';
809
+ index += 1;
810
+ continue;
811
+ }
812
+ if (next === "'") {
813
+ output += "'";
814
+ index += 1;
815
+ continue;
816
+ }
817
+ if (next === "\\") {
818
+ output += "\\";
819
+ index += 1;
820
+ continue;
821
+ }
822
+ }
823
+ output += char;
824
+ }
825
+ return output;
826
+ };
827
+ var normalizeJsLikeJson = (input) => {
828
+ const normalizedQuotes = normalizeSmartQuotes(input);
829
+ const normalizedSingleQuotes = convertSingleQuotedStrings(normalizedQuotes);
830
+ const withQuotedKeys = quoteBareObjectKeys(normalizedSingleQuotes);
831
+ return stripTrailingCommas(withQuotedKeys);
832
+ };
833
+ var extractFirstJsonObject = (input) => {
834
+ const start = input.indexOf("{");
835
+ if (start === -1) return null;
836
+ let depth = 0;
837
+ let inString = false;
838
+ let quoteChar = "";
839
+ let escaped = false;
840
+ for (let index = start; index < input.length; index += 1) {
841
+ const char = input[index];
842
+ if (inString) {
843
+ if (escaped) {
844
+ escaped = false;
845
+ continue;
846
+ }
847
+ if (char === "\\") {
848
+ escaped = true;
849
+ continue;
850
+ }
851
+ if (char === quoteChar) {
852
+ inString = false;
853
+ quoteChar = "";
854
+ }
855
+ continue;
856
+ }
857
+ if (char === '"' || char === "'") {
858
+ inString = true;
859
+ quoteChar = char;
860
+ continue;
861
+ }
862
+ if (char === "{") {
863
+ depth += 1;
864
+ continue;
865
+ }
866
+ if (char === "}") {
867
+ depth -= 1;
868
+ if (depth === 0) {
869
+ return input.slice(start, index + 1);
870
+ }
871
+ }
872
+ }
873
+ return null;
874
+ };
875
+ var parseMaybeNestedJson = (input) => {
876
+ let current = input;
877
+ for (let depth = 0; depth < TOOL_ARGS_CANDIDATE_PARSE_DEPTH; depth += 1) {
878
+ if (typeof current !== "string") {
879
+ return current;
880
+ }
881
+ const trimmed = current.trim();
882
+ if (!trimmed) {
883
+ return {};
884
+ }
885
+ current = JSON.parse(trimmed);
886
+ }
887
+ return current;
888
+ };
889
+ var addCandidate = (candidates, value) => {
890
+ if (typeof value !== "string") return;
891
+ const trimmed = value.trim();
892
+ if (!trimmed) return;
893
+ candidates.add(trimmed);
894
+ };
895
+ var buildProgressiveQuoteUnescapes = (input, rounds = 4) => {
896
+ const values = [];
897
+ let current = input;
898
+ for (let round = 0; round < rounds; round += 1) {
899
+ const next = current.replace(/\\"/gu, '"');
900
+ if (next === current) {
901
+ break;
902
+ }
903
+ values.push(next);
904
+ current = next;
905
+ }
906
+ return values;
907
+ };
908
+ var parseToolArguments = (rawArgs) => {
909
+ if (isPlainObject(rawArgs)) {
910
+ return rawArgs;
911
+ }
912
+ if (typeof rawArgs !== "string") {
913
+ return {};
914
+ }
915
+ const base = rawArgs.trim();
916
+ if (!base) {
917
+ return {};
918
+ }
919
+ const codeFenceStripped = stripMarkdownCodeFence(base);
920
+ const extracted = extractFirstJsonObject(codeFenceStripped);
921
+ const candidates = /* @__PURE__ */ new Set();
922
+ addCandidate(candidates, base);
923
+ addCandidate(candidates, codeFenceStripped);
924
+ addCandidate(candidates, stripOuterQuotes(codeFenceStripped));
925
+ buildProgressiveQuoteUnescapes(base).forEach((candidate) => addCandidate(candidates, candidate));
926
+ buildProgressiveQuoteUnescapes(codeFenceStripped).forEach(
927
+ (candidate) => addCandidate(candidates, candidate)
928
+ );
929
+ addCandidate(candidates, extracted);
930
+ buildProgressiveQuoteUnescapes(extracted || "").forEach(
931
+ (candidate) => addCandidate(candidates, candidate)
932
+ );
933
+ addCandidate(candidates, normalizeJsLikeJson(codeFenceStripped));
934
+ addCandidate(candidates, extracted ? normalizeJsLikeJson(extracted) : null);
935
+ Array.from(candidates).forEach((candidate) => {
936
+ addCandidate(candidates, normalizeEscapesOutsideStrings(candidate));
937
+ });
938
+ for (const candidate of candidates) {
939
+ try {
940
+ const parsed = parseMaybeNestedJson(candidate);
941
+ if (isPlainObject(parsed)) {
942
+ return parsed;
943
+ }
944
+ } catch (e) {
945
+ continue;
946
+ }
947
+ }
948
+ return null;
949
+ };
950
+
655
951
  // src/ChatPanel.tsx
656
952
  var ChatPanel = ({
657
953
  project_id,
@@ -697,7 +993,8 @@ var ChatPanel = ({
697
993
  customerEmailCaptureMode = "HIDE",
698
994
  customerEmailCapturePlaceholder = "Please enter your email...",
699
995
  mcpServers,
700
- progressiveActions = true
996
+ progressiveActions = true,
997
+ resolveMcpAuthHeaders
701
998
  }) => {
702
999
  var _a;
703
1000
  const isEmailAddress = (email) => {
@@ -1023,6 +1320,44 @@ var ChatPanel = ({
1023
1320
  const [toolList, setToolList] = useState5([]);
1024
1321
  const [toolsLoading, setToolsLoading] = useState5(false);
1025
1322
  const [toolsFetchError, setToolsFetchError] = useState5(false);
1323
+ const buildMcpRequestHeaders = useCallback(
1324
+ (_0) => __async(void 0, [_0], function* ({
1325
+ phase,
1326
+ mcpServer,
1327
+ toolName,
1328
+ toolArgs
1329
+ }) {
1330
+ const merged = {};
1331
+ const baseAccessToken = typeof mcpServer.accessToken === "string" ? mcpServer.accessToken.trim() : "";
1332
+ if (baseAccessToken) {
1333
+ merged["x-mcp-access-token"] = baseAccessToken;
1334
+ }
1335
+ if (project_id) {
1336
+ merged["x-project-id"] = project_id;
1337
+ }
1338
+ if (!resolveMcpAuthHeaders) return merged;
1339
+ try {
1340
+ const resolved = yield resolveMcpAuthHeaders({
1341
+ phase,
1342
+ mcpServer,
1343
+ projectId: project_id,
1344
+ customer: currentCustomer,
1345
+ toolName,
1346
+ toolArgs
1347
+ });
1348
+ return __spreadValues(__spreadValues({}, merged), normalizeMcpHeaders(
1349
+ resolved
1350
+ ));
1351
+ } catch (error2) {
1352
+ console.error(
1353
+ `Failed to resolve MCP auth headers for ${phase} request:`,
1354
+ error2
1355
+ );
1356
+ return merged;
1357
+ }
1358
+ }),
1359
+ [project_id, currentCustomer, resolveMcpAuthHeaders]
1360
+ );
1026
1361
  useEffect6(() => {
1027
1362
  const fetchAndSetTools = () => __async(void 0, null, function* () {
1028
1363
  if (!mcpServers || mcpServers.length === 0) {
@@ -1039,7 +1374,13 @@ var ChatPanel = ({
1039
1374
  m.url
1040
1375
  )}`;
1041
1376
  try {
1042
- const response2 = yield fetch(urlToFetch);
1377
+ const requestHeaders = yield buildMcpRequestHeaders({
1378
+ phase: "list",
1379
+ mcpServer: m
1380
+ });
1381
+ const response2 = yield fetch(urlToFetch, {
1382
+ headers: requestHeaders
1383
+ });
1043
1384
  if (!response2.ok) {
1044
1385
  console.error(
1045
1386
  `Error fetching tools from ${m.url}: ${response2.status} ${response2.statusText}`
@@ -1055,7 +1396,7 @@ var ChatPanel = ({
1055
1396
  return toolsFromServer.map((tool) => __spreadProps(__spreadValues({}, tool), {
1056
1397
  url: m.url,
1057
1398
  accessToken: m.accessToken || "",
1058
- headers: {}
1399
+ headers: requestHeaders
1059
1400
  }));
1060
1401
  } else {
1061
1402
  return [];
@@ -1084,7 +1425,7 @@ var ChatPanel = ({
1084
1425
  }
1085
1426
  });
1086
1427
  fetchAndSetTools();
1087
- }, [mcpServers, publicAPIUrl]);
1428
+ }, [mcpServers, publicAPIUrl, buildMcpRequestHeaders]);
1088
1429
  const llmResult = useLLM({
1089
1430
  project_id,
1090
1431
  customer: currentCustomer,
@@ -1449,19 +1790,19 @@ var ChatPanel = ({
1449
1790
  []
1450
1791
  );
1451
1792
  const anthropic_toolAction = {
1452
- pattern: '\\{"type":"tool_use","id":"([^"]+)","name":"([^"]+)","input":(\\{[\\s\\S]+?\\}),"service":"([^"]+)"\\}',
1793
+ pattern: '\\{"type":"tool_use","id":"([^"]+)","name":"([^"]+)","input":(\\{[\\s\\S]*?\\}),"service":"([^"]+)"\\}',
1453
1794
  type: "markdown",
1454
1795
  markdown: "<br />*Tool use requested: $2*",
1455
1796
  actionType: "tool"
1456
1797
  };
1457
1798
  const openAI_toolAction = {
1458
- pattern: '\\{"id":"([^"]+)","type":"function","function":\\{"name":"([^"]+)","arguments":"((?:\\\\.|[^"\\\\])*)"\\},"service":"([^"]+)"\\}',
1799
+ pattern: '\\{"id":"([^"]+)","type":"function","function":\\{"name":"([^"]+)","arguments":"([\\s\\S]*?)"\\},"service":"([^"]+)"\\}',
1459
1800
  type: "markdown",
1460
1801
  markdown: "<br />*Tool use requested: $2*",
1461
1802
  actionType: "tool"
1462
1803
  };
1463
1804
  const google_toolAction = {
1464
- 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*\\}$',
1805
+ 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*\\}$',
1465
1806
  type: "markdown",
1466
1807
  markdown: "<br />*Tool use requested: $2*",
1467
1808
  actionType: "tool"
@@ -1603,114 +1944,125 @@ var ChatPanel = ({
1603
1944
  ]
1604
1945
  }
1605
1946
  ];
1606
- const toolCallsMessage = {
1607
- role: "assistant",
1608
- content: [],
1609
- tool_calls: []
1610
- };
1611
- const toolCallsPromises = toolsToProcess.map((req) => __async(void 0, null, function* () {
1612
- if (!req) return null;
1613
- try {
1947
+ const parsedToolCalls = yield Promise.all(
1948
+ toolsToProcess.map((req, index) => __async(void 0, null, function* () {
1949
+ var _a2, _b, _c, _d, _e, _f;
1950
+ if (!req) return null;
1951
+ let parsedToolCall = null;
1952
+ try {
1953
+ parsedToolCall = JSON.parse(req.match);
1954
+ } catch (error2) {
1955
+ console.error("Failed to parse tool call:", error2);
1956
+ }
1957
+ 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 : "");
1958
+ if (!toolName) return null;
1959
+ const rawCallId = req.groups[0] || (parsedToolCall == null ? void 0 : parsedToolCall.id) || (parsedToolCall == null ? void 0 : parsedToolCall.tool_call_id) || `${toolName}-${index + 1}`;
1960
+ const callId = typeof rawCallId === "string" && rawCallId.trim().length > 0 && rawCallId !== "functionCall" ? rawCallId : `${toolName}-${index + 1}`;
1961
+ let args = {};
1962
+ 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 : "{}";
1963
+ const parsedArgs = parseToolArguments(rawArgs);
1964
+ if (!parsedArgs) {
1965
+ console.error("Failed to parse tool arguments", {
1966
+ toolName,
1967
+ callId,
1968
+ rawArgsPreview: typeof rawArgs === "string" ? rawArgs.slice(0, 500) : JSON.stringify(rawArgs).slice(0, 500)
1969
+ });
1970
+ return null;
1971
+ }
1972
+ args = parsedArgs;
1973
+ const serviceTag = typeof req.groups[3] === "string" && req.groups[3] || typeof (parsedToolCall == null ? void 0 : parsedToolCall.service) === "string" && parsedToolCall.service || "";
1614
1974
  return {
1615
1975
  req,
1616
- parsedToolCall: JSON.parse(req.match)
1976
+ toolName,
1977
+ callId,
1978
+ args,
1979
+ serviceTag
1617
1980
  };
1618
- } catch (e) {
1619
- console.error("Failed to parse tool call:", e);
1620
- return null;
1621
- }
1981
+ }))
1982
+ );
1983
+ const toolCallBatch = parsedToolCalls.filter(Boolean);
1984
+ const finalToolCalls = toolCallBatch.map((toolCall) => ({
1985
+ id: toolCall.callId,
1986
+ type: "tool_use",
1987
+ name: toolCall.toolName,
1988
+ input: toolCall.args,
1989
+ service: toolCall.serviceTag
1622
1990
  }));
1623
- const parsedToolCalls = yield Promise.all(toolCallsPromises);
1624
- parsedToolCalls.forEach((item) => {
1625
- if (item && item.parsedToolCall) {
1626
- toolCallsMessage.tool_calls.push(item.parsedToolCall);
1627
- }
1628
- });
1629
- newMessages.push(toolCallsMessage);
1630
- const finalToolCalls = toolCallsMessage.tool_calls;
1631
- const toolResponsePromises = parsedToolCalls.map((item) => __async(void 0, null, function* () {
1632
- var _a2;
1633
- if (!item || !item.req) return null;
1634
- const req = item.req;
1635
- const mcpTool = toolList.find((tool) => tool.name === req.toolName);
1991
+ const toolResponsePromises = toolCallBatch.map((toolCall) => __async(void 0, null, function* () {
1992
+ var _a2, _b, _c;
1993
+ const mcpTool = toolList.find((tool) => tool.name === toolCall.toolName);
1636
1994
  if (!mcpTool) {
1637
- console.error(`Tool ${req.toolName} not found in tool list`);
1638
- return null;
1995
+ console.error(`Tool ${toolCall.toolName} not found in tool list`);
1996
+ return {
1997
+ tool_call_id: toolCall.callId,
1998
+ tool_name: toolCall.toolName,
1999
+ result: `Tool ${toolCall.toolName} not found in current tool list.`,
2000
+ isError: true
2001
+ };
1639
2002
  }
1640
2003
  try {
1641
- let args;
1642
- try {
1643
- args = JSON.parse(req.groups[2]);
1644
- } catch (e) {
1645
- try {
1646
- args = JSON.parse(req.groups[2].replace(/\\"/g, '"'));
1647
- } catch (err) {
1648
- console.error("Failed to parse tool arguments:", err);
1649
- return null;
1650
- }
1651
- }
1652
2004
  const body = {
1653
- tool: req.groups[1],
1654
- args
2005
+ tool: toolCall.toolName,
2006
+ args: toolCall.args
1655
2007
  };
1656
2008
  const result = yield fetch(
1657
2009
  `${publicAPIUrl}/tools/${encodeURIComponent(mcpTool.url)}`,
1658
2010
  {
1659
2011
  method: "POST",
1660
- headers: {
1661
- "Content-Type": "application/json",
1662
- "x-mcp-access-token": mcpTool.accessToken && mcpTool.accessToken !== "" ? mcpTool.accessToken : "",
1663
- "x-project-id": project_id
1664
- },
2012
+ headers: __spreadValues({
2013
+ "Content-Type": "application/json"
2014
+ }, yield buildMcpRequestHeaders({
2015
+ phase: "call",
2016
+ mcpServer: mcpTool,
2017
+ toolName: toolCall.toolName,
2018
+ toolArgs: toolCall.args
2019
+ })),
1665
2020
  body: JSON.stringify(body)
1666
2021
  }
1667
2022
  );
1668
2023
  if (!result.ok) {
1669
2024
  console.error(
1670
- `Error calling tool ${req.toolName}: ${result.status} ${result.statusText}`
2025
+ `Error calling tool ${toolCall.toolName}: ${result.status} ${result.statusText}`
1671
2026
  );
1672
2027
  const errorBody = yield result.text();
1673
2028
  console.error(`Error body: ${errorBody}`);
1674
- return null;
2029
+ return {
2030
+ tool_call_id: toolCall.callId,
2031
+ tool_name: toolCall.toolName,
2032
+ result: `HTTP ${result.status} ${result.statusText}: ${errorBody || "Tool call failed"}`,
2033
+ isError: true
2034
+ };
1675
2035
  }
1676
2036
  let resultData;
1677
2037
  try {
1678
2038
  resultData = yield result.json();
1679
2039
  } catch (jsonError) {
1680
2040
  console.error(
1681
- `Error parsing JSON response for tool ${req.toolName}:`,
2041
+ `Error parsing JSON response for tool ${toolCall.toolName}:`,
1682
2042
  jsonError
1683
2043
  );
1684
- try {
1685
- const textBody = yield result.text();
1686
- console.error("Response body (text):", textBody);
1687
- } catch (textError) {
1688
- console.error(
1689
- "Failed to read response body as text either:",
1690
- textError
1691
- );
1692
- }
1693
- return null;
1694
- }
1695
- if (resultData && resultData.content && resultData.content.length > 0) {
1696
- const textResult = (_a2 = resultData.content[0]) == null ? void 0 : _a2.text;
1697
2044
  return {
1698
- role: "tool",
1699
- content: [
1700
- {
1701
- type: "text",
1702
- text: textResult
1703
- }
1704
- ],
1705
- tool_call_id: req.groups[0]
2045
+ tool_call_id: toolCall.callId,
2046
+ tool_name: toolCall.toolName,
2047
+ result: "Tool returned a non-JSON response.",
2048
+ isError: true
1706
2049
  };
1707
- } else {
1708
- console.error(`No content returned from tool ${req.toolName}`);
1709
- return null;
1710
2050
  }
2051
+ 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);
2052
+ return {
2053
+ tool_call_id: toolCall.callId,
2054
+ tool_name: toolCall.toolName,
2055
+ result: textResult || "",
2056
+ isError: (resultData == null ? void 0 : resultData.isError) === true
2057
+ };
1711
2058
  } catch (error2) {
1712
- console.error(`Error processing tool ${req.toolName}:`, error2);
1713
- return null;
2059
+ console.error(`Error processing tool ${toolCall.toolName}:`, error2);
2060
+ return {
2061
+ tool_call_id: toolCall.callId,
2062
+ tool_name: toolCall.toolName,
2063
+ result: error2 instanceof Error ? error2.message : `Unhandled error calling ${toolCall.toolName}`,
2064
+ isError: true
2065
+ };
1714
2066
  }
1715
2067
  }));
1716
2068
  const toolResponses = yield Promise.all(toolResponsePromises);
@@ -1732,11 +2084,49 @@ var ChatPanel = ({
1732
2084
  });
1733
2085
  });
1734
2086
  }
1735
- finalToolResponses.forEach((response2) => {
1736
- if (response2) {
1737
- newMessages.push(response2);
1738
- }
1739
- });
2087
+ const toReplayText = (value, maxLength = 2e3) => {
2088
+ const raw = typeof value === "string" ? value : (() => {
2089
+ try {
2090
+ return JSON.stringify(value);
2091
+ } catch (_error) {
2092
+ return String(value != null ? value : "");
2093
+ }
2094
+ })();
2095
+ const normalized = String(raw != null ? raw : "").replace(/\s+/g, " ").trim();
2096
+ if (normalized.length <= maxLength) return normalized;
2097
+ return `${normalized.slice(0, maxLength - 3)}...`;
2098
+ };
2099
+ if (toolCallBatch.length > 0) {
2100
+ const replayLines = toolCallBatch.map((toolCall, index) => {
2101
+ var _a2;
2102
+ const matchedResponse = finalToolResponses.find(
2103
+ (response2) => (response2 == null ? void 0 : response2.tool_call_id) === toolCall.callId
2104
+ ) || finalToolResponses[index];
2105
+ const status = (matchedResponse == null ? void 0 : matchedResponse.isError) ? "error" : "ok";
2106
+ const resultText = toReplayText((_a2 = matchedResponse == null ? void 0 : matchedResponse.result) != null ? _a2 : "No result returned");
2107
+ return [
2108
+ `Tool: ${toolCall.toolName}`,
2109
+ `Call ID: ${toolCall.callId}`,
2110
+ `Status: ${status}`,
2111
+ `Args: ${toReplayText(toolCall.args, 600)}`,
2112
+ `Result: ${resultText}`
2113
+ ].join("\n");
2114
+ });
2115
+ newMessages.push({
2116
+ role: "user",
2117
+ content: [
2118
+ {
2119
+ type: "text",
2120
+ text: [
2121
+ "Tool execution summary for the previous request:",
2122
+ ...replayLines,
2123
+ "Continue the same assistant response from exactly where you paused using these tool results.",
2124
+ "If this response is using meta tags, keep the same format (<thinking>, <reasoning>, <searching>) in the continuation."
2125
+ ].join("\n\n")
2126
+ }
2127
+ ]
2128
+ });
2129
+ }
1740
2130
  send(
1741
2131
  "",
1742
2132
  newMessages,
@@ -3518,7 +3908,8 @@ var AgentPanel = ({
3518
3908
  //ragQueryLimit = 10,
3519
3909
  //ragRankLimit = 5,
3520
3910
  initialHistory = {},
3521
- hideRagContextInPrompt = true
3911
+ hideRagContextInPrompt = true,
3912
+ resolveMcpAuthHeaders
3522
3913
  }) => {
3523
3914
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u;
3524
3915
  const searchParams = new URLSearchParams(location.search);
@@ -3656,7 +4047,8 @@ var AgentPanel = ({
3656
4047
  createConversationOnFirstChat: (_s = agentData == null ? void 0 : agentData.createConversationOnFirstChat) != null ? _s : true,
3657
4048
  customerEmailCaptureMode: (_t = agentData == null ? void 0 : agentData.customerEmailCaptureMode) != null ? _t : "HIDE",
3658
4049
  customerEmailCapturePlaceholder: (_u = agentData == null ? void 0 : agentData.customerEmailCapturePlaceholder) != null ? _u : "Please enter your email...",
3659
- mcpServers: mcpData
4050
+ mcpServers: mcpData,
4051
+ resolveMcpAuthHeaders
3660
4052
  })
3661
4053
  ));
3662
4054
  };
@@ -3733,18 +4125,731 @@ var ToolInfoModal2 = ({
3733
4125
  var ToolInfoModal_default2 = ToolInfoModal2;
3734
4126
 
3735
4127
  // src/AIChatPanel.tsx
4128
+ var areToolRequestListsEqual = (a, b) => {
4129
+ if (a.length !== b.length) return false;
4130
+ for (let index = 0; index < a.length; index += 1) {
4131
+ const left = a[index];
4132
+ const right = b[index];
4133
+ if (!left || !right) return false;
4134
+ if (left.callId !== right.callId) return false;
4135
+ if (left.toolName !== right.toolName) return false;
4136
+ if (left.serviceTag !== right.serviceTag) return false;
4137
+ if (left.match !== right.match) return false;
4138
+ if (left.start !== right.start || left.end !== right.end) return false;
4139
+ }
4140
+ return true;
4141
+ };
4142
+ var mergeContinuationResponseText = (baseText, continuationText) => {
4143
+ const base = typeof baseText === "string" ? baseText : "";
4144
+ const continuation = typeof continuationText === "string" ? continuationText : "";
4145
+ if (!base) return continuation;
4146
+ if (!continuation) return base;
4147
+ if (base.includes(continuation)) {
4148
+ return base;
4149
+ }
4150
+ const maxOverlap = Math.min(base.length, continuation.length);
4151
+ for (let overlap = maxOverlap; overlap > 0; overlap -= 1) {
4152
+ if (base.slice(-overlap) === continuation.slice(0, overlap)) {
4153
+ return `${base}${continuation.slice(overlap)}`;
4154
+ }
4155
+ }
4156
+ return `${base}
4157
+
4158
+ ${continuation}`;
4159
+ };
4160
+ var INLINE_TOOL_MARKER_PREFIX = "[[AI_TOOL_CALL:";
4161
+ var INLINE_TOOL_MARKER_SUFFIX = "]]";
4162
+ var INLINE_TOOL_MARKER_REGEX = /\[\[AI_TOOL_CALL:([^|\]]+)\|([^\]]+)\]\]/g;
4163
+ var INLINE_THINKING_MARKER_PREFIX = "[[AI_THINK_BLOCK:";
4164
+ var INLINE_THINKING_MARKER_SUFFIX = "]]";
4165
+ var INLINE_THINKING_MARKER_REGEX = /\[\[AI_THINK_BLOCK:([^|\]]+)\|([^\]]+)\]\]/g;
4166
+ var MAX_TOOL_CONTINUATIONS_PER_TURN = 20;
4167
+ var MAX_TOOL_REPLAY_PAYLOAD_CHARS = 75e4;
4168
+ var MAX_TRACE_SUMMARY_CHARS = 1800;
4169
+ var MAX_TRACE_REASONING_BLOCKS = 4;
4170
+ var MAX_TRACE_TOOL_LINES = 6;
4171
+ var MAX_TRACE_ITEM_CHARS = 220;
4172
+ var MAX_TRACE_LINE_CHARS = 420;
4173
+ var hasInlineRuntimeMarkers = (value) => {
4174
+ const source = typeof value === "string" ? value : "";
4175
+ return source.includes(INLINE_TOOL_MARKER_PREFIX) || source.includes(INLINE_THINKING_MARKER_PREFIX);
4176
+ };
4177
+ var toNonEmptyLines = (value) => String(value || "").split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
4178
+ var isBoundarySubsetByLines = (existingLines, incomingLines) => {
4179
+ if (incomingLines.length === 0 || existingLines.length === 0) return false;
4180
+ if (incomingLines.length >= existingLines.length) return false;
4181
+ const prefixMatch = incomingLines.every((line, index) => existingLines[index] === line);
4182
+ if (prefixMatch) return true;
4183
+ const suffixStart = existingLines.length - incomingLines.length;
4184
+ return incomingLines.every((line, index) => existingLines[suffixStart + index] === line);
4185
+ };
4186
+ var shouldPreserveBoundaryDroppedStreamText = (existingContent, incomingContent) => {
4187
+ const existing = String(existingContent || "").trim();
4188
+ const incoming = String(incomingContent || "").trim();
4189
+ if (!existing) return false;
4190
+ if (!incoming) return true;
4191
+ if (incoming === existing) return false;
4192
+ if (!hasInlineRuntimeMarkers(existing)) return false;
4193
+ if (existing.includes(incoming)) {
4194
+ return true;
4195
+ }
4196
+ const existingLines = toNonEmptyLines(existing);
4197
+ const incomingLines = toNonEmptyLines(incoming);
4198
+ return isBoundarySubsetByLines(existingLines, incomingLines);
4199
+ };
4200
+ var isObjectRecord = (value) => !!value && typeof value === "object" && !Array.isArray(value);
4201
+ var stringifyToolArgs = (value) => {
4202
+ if (typeof value === "string") return value;
4203
+ try {
4204
+ return JSON.stringify(value != null ? value : {});
4205
+ } catch (_error) {
4206
+ return "{}";
4207
+ }
4208
+ };
4209
+ var truncateTraceText = (value, maxChars) => {
4210
+ const text = String(value || "").trim();
4211
+ if (!text) return "";
4212
+ if (text.length <= maxChars) return text;
4213
+ return `${text.slice(0, Math.max(0, maxChars - 1)).trimEnd()}\u2026`;
4214
+ };
4215
+ var normalizeTraceText = (value) => {
4216
+ if (value === null || value === void 0) return "";
4217
+ const raw = typeof value === "string" ? value : (() => {
4218
+ try {
4219
+ return JSON.stringify(value);
4220
+ } catch (_error) {
4221
+ return String(value);
4222
+ }
4223
+ })();
4224
+ return String(raw || "").replace(/\s+/g, " ").trim();
4225
+ };
4226
+ var toTraceObjectArray = (value) => {
4227
+ if (!Array.isArray(value)) return [];
4228
+ return value.filter((item) => isObjectRecord(item));
4229
+ };
4230
+ var getTraceStatusLabel = (response) => {
4231
+ if (!response) return "pending";
4232
+ if (response.isError === true || response.error === true) return "error";
4233
+ return "ok";
4234
+ };
4235
+ var buildCompactTraceSummary = ({
4236
+ reasoningBlocks,
4237
+ toolCalls,
4238
+ toolResponses
4239
+ }) => {
4240
+ const sections = [];
4241
+ 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) => {
4242
+ const content = truncateTraceText(normalizeTraceText(block.content), MAX_TRACE_ITEM_CHARS);
4243
+ if (!content) return "";
4244
+ const type = typeof block.type === "string" ? block.type : "thinking";
4245
+ return `- ${type}: ${content}`;
4246
+ }).filter(Boolean);
4247
+ if (normalizedReasoning.length > 0) {
4248
+ sections.push(["reasoning:", ...normalizedReasoning].join("\n"));
4249
+ }
4250
+ const calls = toTraceObjectArray(toolCalls).slice(-MAX_TRACE_TOOL_LINES);
4251
+ const responses = toTraceObjectArray(toolResponses);
4252
+ if (calls.length > 0) {
4253
+ const responsesByCallId = /* @__PURE__ */ new Map();
4254
+ responses.forEach((response) => {
4255
+ const key = typeof response.tool_call_id === "string" ? response.tool_call_id.trim() : "";
4256
+ if (!key || responsesByCallId.has(key)) return;
4257
+ responsesByCallId.set(key, response);
4258
+ });
4259
+ const responseOffset = Math.max(0, responses.length - calls.length);
4260
+ const toolLines = calls.map((call, index) => {
4261
+ var _a, _b, _c, _d, _e, _f;
4262
+ const toolName = typeof call.name === "string" ? call.name.trim() : typeof call.tool_name === "string" ? call.tool_name.trim() : "tool";
4263
+ const callId = typeof call.id === "string" ? call.id.trim() : typeof call.tool_call_id === "string" ? call.tool_call_id.trim() : "";
4264
+ const response = (callId ? responsesByCallId.get(callId) : void 0) || responses[responseOffset + index] || responses[index];
4265
+ const status = getTraceStatusLabel(response);
4266
+ const rawArgs = (_c = (_b = (_a = call.input) != null ? _a : call.args) != null ? _b : call.arguments) != null ? _c : {};
4267
+ const parsedArgs = parseToolArguments(rawArgs);
4268
+ const normalizedArgs = truncateTraceText(
4269
+ normalizeTraceText(parsedArgs != null ? parsedArgs : isObjectRecord(rawArgs) ? rawArgs : rawArgs || {}),
4270
+ MAX_TRACE_ITEM_CHARS
4271
+ );
4272
+ const normalizedResult = truncateTraceText(
4273
+ 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 : ""),
4274
+ MAX_TRACE_ITEM_CHARS
4275
+ );
4276
+ const base = callId ? `- ${toolName} (${callId}) ${status}` : `- ${toolName} ${status}`;
4277
+ const withArgs = normalizedArgs ? `${base} args=${normalizedArgs}` : base;
4278
+ const withResult = normalizedResult ? `${withArgs} result=${normalizedResult}` : withArgs;
4279
+ return truncateTraceText(withResult, MAX_TRACE_LINE_CHARS);
4280
+ }).filter(Boolean);
4281
+ if (toolLines.length > 0) {
4282
+ sections.push(["tools:", ...toolLines].join("\n"));
4283
+ }
4284
+ }
4285
+ if (sections.length === 0) return "";
4286
+ return truncateTraceText(["TRACE SUMMARY (compact)", ...sections].join("\n"), MAX_TRACE_SUMMARY_CHARS);
4287
+ };
4288
+ var findPreviousNonWhitespaceChar = (text, startIndex) => {
4289
+ for (let index = startIndex; index >= 0; index -= 1) {
4290
+ const char = text[index];
4291
+ if (typeof char !== "string") continue;
4292
+ if (!/\s/.test(char)) {
4293
+ return char;
4294
+ }
4295
+ }
4296
+ return null;
4297
+ };
4298
+ var findNextNonWhitespaceChar = (text, startIndex) => {
4299
+ for (let index = startIndex; index < text.length; index += 1) {
4300
+ const char = text[index];
4301
+ if (typeof char !== "string") continue;
4302
+ if (!/\s/.test(char)) {
4303
+ return char;
4304
+ }
4305
+ }
4306
+ return null;
4307
+ };
4308
+ var isStandaloneToolObjectSegment = (text, openIndex, closeIndex) => {
4309
+ const lineStart = text.lastIndexOf("\n", openIndex - 1) + 1;
4310
+ const lineEndRaw = text.indexOf("\n", closeIndex + 1);
4311
+ const lineEnd = lineEndRaw === -1 ? text.length : lineEndRaw;
4312
+ const linePrefix = text.slice(lineStart, openIndex);
4313
+ const lineSuffix = text.slice(closeIndex + 1, lineEnd);
4314
+ const startsLine = linePrefix.trim().length === 0;
4315
+ const endsLine = lineSuffix.trim().length === 0;
4316
+ const prevChar = findPreviousNonWhitespaceChar(text, openIndex - 1);
4317
+ const nextChar = findNextNonWhitespaceChar(text, closeIndex + 1);
4318
+ const prevDelimited = prevChar !== null && ["{", "}", "[", "]", ","].includes(prevChar);
4319
+ const nextDelimited = nextChar !== null && ["{", "}", "[", "]", ","].includes(nextChar);
4320
+ const prefixWindow = text.slice(Math.max(0, openIndex - 64), openIndex);
4321
+ const hasLabelPrefix = /(?:^|[\r\n])\s*[A-Za-z][A-Za-z0-9 _-]{0,30}:\s*$/.test(prefixWindow);
4322
+ if (hasLabelPrefix) return false;
4323
+ return (startsLine || prevDelimited) && (endsLine || nextDelimited);
4324
+ };
4325
+ var extractTopLevelJsonObjectSegments = (source) => {
4326
+ const text = typeof source === "string" ? source : "";
4327
+ if (!text) return [];
4328
+ const segments = [];
4329
+ let cursor = 0;
4330
+ while (cursor < text.length) {
4331
+ const openIndex = text.indexOf("{", cursor);
4332
+ if (openIndex === -1) break;
4333
+ let depth = 0;
4334
+ let inString = false;
4335
+ let escaped = false;
4336
+ let closeIndex = -1;
4337
+ for (let index = openIndex; index < text.length; index += 1) {
4338
+ const char = text[index];
4339
+ if (inString) {
4340
+ if (escaped) {
4341
+ escaped = false;
4342
+ continue;
4343
+ }
4344
+ if (char === "\\") {
4345
+ escaped = true;
4346
+ continue;
4347
+ }
4348
+ if (char === '"') {
4349
+ inString = false;
4350
+ }
4351
+ continue;
4352
+ }
4353
+ if (char === '"') {
4354
+ inString = true;
4355
+ continue;
4356
+ }
4357
+ if (char === "{") {
4358
+ depth += 1;
4359
+ continue;
4360
+ }
4361
+ if (char === "}") {
4362
+ depth -= 1;
4363
+ if (depth === 0) {
4364
+ closeIndex = index;
4365
+ break;
4366
+ }
4367
+ if (depth < 0) {
4368
+ break;
4369
+ }
4370
+ }
4371
+ }
4372
+ if (closeIndex === -1) {
4373
+ cursor = openIndex + 1;
4374
+ continue;
4375
+ }
4376
+ const raw = text.slice(openIndex, closeIndex + 1);
4377
+ try {
4378
+ const value = JSON.parse(raw);
4379
+ if (isObjectRecord(value) && isStandaloneToolObjectSegment(text, openIndex, closeIndex)) {
4380
+ segments.push({
4381
+ start: openIndex,
4382
+ end: closeIndex + 1,
4383
+ raw,
4384
+ value
4385
+ });
4386
+ }
4387
+ } catch (_error) {
4388
+ }
4389
+ cursor = closeIndex + 1;
4390
+ }
4391
+ return segments;
4392
+ };
4393
+ var resolveToolRequestFromSegment = (segment, fallbackIndex) => {
4394
+ var _a, _b, _c;
4395
+ const record = segment.value;
4396
+ const serviceTag = typeof record.service === "string" ? record.service : "";
4397
+ const rawId = typeof record.id === "string" ? record.id.trim() : "";
4398
+ if (record.type === "tool_use" && typeof record.name === "string") {
4399
+ const toolName = String(record.name).trim();
4400
+ if (!toolName) return null;
4401
+ const callId = rawId || `${toolName}-${fallbackIndex + 1}`;
4402
+ return {
4403
+ match: segment.raw,
4404
+ groups: [callId, toolName, stringifyToolArgs((_a = record.input) != null ? _a : {}), serviceTag],
4405
+ toolName,
4406
+ callId,
4407
+ serviceTag,
4408
+ start: segment.start,
4409
+ end: segment.end
4410
+ };
4411
+ }
4412
+ if (record.type === "function" && isObjectRecord(record.function)) {
4413
+ const functionRecord = record.function;
4414
+ const toolName = typeof functionRecord.name === "string" ? functionRecord.name.trim() : "";
4415
+ if (!toolName) return null;
4416
+ const callId = rawId || `${toolName}-${fallbackIndex + 1}`;
4417
+ return {
4418
+ match: segment.raw,
4419
+ groups: [callId, toolName, stringifyToolArgs((_b = functionRecord.arguments) != null ? _b : {}), serviceTag],
4420
+ toolName,
4421
+ callId,
4422
+ serviceTag,
4423
+ start: segment.start,
4424
+ end: segment.end
4425
+ };
4426
+ }
4427
+ if (isObjectRecord(record.functionCall) && typeof record.functionCall.name === "string") {
4428
+ const functionCall = record.functionCall;
4429
+ const toolName = String(functionCall.name).trim();
4430
+ if (!toolName) return null;
4431
+ const callId = rawId || `${toolName}-${fallbackIndex + 1}`;
4432
+ return {
4433
+ match: segment.raw,
4434
+ groups: [callId, toolName, stringifyToolArgs((_c = functionCall.args) != null ? _c : {}), serviceTag],
4435
+ toolName,
4436
+ callId,
4437
+ serviceTag,
4438
+ start: segment.start,
4439
+ end: segment.end
4440
+ };
4441
+ }
4442
+ return null;
4443
+ };
4444
+ var extractBalancedObjectCandidate = (input) => {
4445
+ const text = typeof input === "string" ? input : "";
4446
+ const start = text.indexOf("{");
4447
+ if (start === -1) return null;
4448
+ let depth = 0;
4449
+ let inString = false;
4450
+ let escaped = false;
4451
+ for (let index = start; index < text.length; index += 1) {
4452
+ const char = text[index];
4453
+ if (inString) {
4454
+ if (escaped) {
4455
+ escaped = false;
4456
+ continue;
4457
+ }
4458
+ if (char === "\\") {
4459
+ escaped = true;
4460
+ continue;
4461
+ }
4462
+ if (char === '"') {
4463
+ inString = false;
4464
+ }
4465
+ continue;
4466
+ }
4467
+ if (char === '"') {
4468
+ inString = true;
4469
+ continue;
4470
+ }
4471
+ if (char === "{") {
4472
+ depth += 1;
4473
+ continue;
4474
+ }
4475
+ if (char === "}") {
4476
+ depth -= 1;
4477
+ if (depth === 0) {
4478
+ return text.slice(start, index + 1);
4479
+ }
4480
+ }
4481
+ }
4482
+ return null;
4483
+ };
4484
+ var extractLineLevelFallbackToolRequests = (source, existing) => {
4485
+ const text = typeof source === "string" ? source : "";
4486
+ if (!text) return [];
4487
+ const takenRanges = existing.map((request) => ({
4488
+ start: request.start,
4489
+ end: request.end
4490
+ }));
4491
+ const overlapsExisting = (start, end) => takenRanges.some((range) => start < range.end && end > range.start);
4492
+ const requests = [];
4493
+ let offset = 0;
4494
+ const lines = text.split("\n");
4495
+ const normalizeStandaloneToolJsonLine = (line) => {
4496
+ const trimmed = line.trim();
4497
+ if (!trimmed) return null;
4498
+ let normalized = trimmed;
4499
+ const first = normalized[0];
4500
+ const last = normalized[normalized.length - 1];
4501
+ const hasSymmetricQuote = normalized.length >= 2 && (first === "'" && last === "'" || first === '"' && last === '"' || first === "`" && last === "`");
4502
+ if (hasSymmetricQuote) {
4503
+ const inner = normalized.slice(1, -1).trim();
4504
+ if (first === '"') {
4505
+ try {
4506
+ const decoded = JSON.parse(normalized);
4507
+ if (typeof decoded === "string") {
4508
+ normalized = decoded.trim();
4509
+ } else {
4510
+ normalized = inner;
4511
+ }
4512
+ } catch (_error) {
4513
+ normalized = inner;
4514
+ }
4515
+ } else {
4516
+ normalized = inner;
4517
+ }
4518
+ }
4519
+ const extractedCandidate = extractBalancedObjectCandidate(normalized);
4520
+ if (extractedCandidate) {
4521
+ normalized = extractedCandidate.trim();
4522
+ }
4523
+ if (!normalized.startsWith("{")) return null;
4524
+ if (!normalized.includes('"type"')) return null;
4525
+ return normalized;
4526
+ };
4527
+ const extractBalancedObjectAfterField = (input, fieldName) => {
4528
+ const fieldRegex = new RegExp(`"${fieldName}"\\s*:\\s*`, "i");
4529
+ const fieldMatch = fieldRegex.exec(input);
4530
+ if (!fieldMatch) return null;
4531
+ const startIndex = input.indexOf("{", fieldMatch.index + fieldMatch[0].length);
4532
+ if (startIndex === -1) return null;
4533
+ let depth = 0;
4534
+ let inString = false;
4535
+ let escaped = false;
4536
+ for (let index = startIndex; index < input.length; index += 1) {
4537
+ const char = input[index];
4538
+ if (inString) {
4539
+ if (escaped) {
4540
+ escaped = false;
4541
+ continue;
4542
+ }
4543
+ if (char === "\\") {
4544
+ escaped = true;
4545
+ continue;
4546
+ }
4547
+ if (char === '"') {
4548
+ inString = false;
4549
+ }
4550
+ continue;
4551
+ }
4552
+ if (char === '"') {
4553
+ inString = true;
4554
+ continue;
4555
+ }
4556
+ if (char === "{") {
4557
+ depth += 1;
4558
+ continue;
4559
+ }
4560
+ if (char === "}") {
4561
+ depth -= 1;
4562
+ if (depth === 0) {
4563
+ return input.slice(startIndex, index + 1);
4564
+ }
4565
+ }
4566
+ }
4567
+ return null;
4568
+ };
4569
+ lines.forEach((line, index) => {
4570
+ var _a, _b, _c, _d, _e, _f;
4571
+ const lineStart = offset;
4572
+ const lineEnd = lineStart + line.length;
4573
+ offset = lineEnd + (index < lines.length - 1 ? 1 : 0);
4574
+ const normalized = normalizeStandaloneToolJsonLine(line);
4575
+ if (!normalized) return;
4576
+ if (!(normalized.includes('"tool_use"') || normalized.includes('"function"'))) return;
4577
+ if (/(^|[\r\n])\s*[A-Za-z][A-Za-z0-9 _-]{0,30}:\s*\{/.test(line)) return;
4578
+ if (overlapsExisting(lineStart, lineEnd)) return;
4579
+ try {
4580
+ const parsedLine = JSON.parse(normalized);
4581
+ if (isObjectRecord(parsedLine)) {
4582
+ const type2 = typeof parsedLine.type === "string" ? parsedLine.type.trim().toLowerCase() : "";
4583
+ if (type2 === "tool_use") {
4584
+ const toolName2 = typeof parsedLine.name === "string" ? parsedLine.name.trim() : "";
4585
+ if (!toolName2) return;
4586
+ const callId2 = typeof parsedLine.id === "string" && parsedLine.id.trim().length > 0 ? parsedLine.id.trim() : `tool-call-${index + 1}`;
4587
+ const serviceTag = typeof parsedLine.service === "string" ? parsedLine.service : "";
4588
+ const parsedArgs2 = parseToolArguments((_a = parsedLine.input) != null ? _a : {});
4589
+ if (!parsedArgs2) return;
4590
+ requests.push({
4591
+ match: line,
4592
+ groups: [callId2, toolName2, stringifyToolArgs(parsedArgs2), serviceTag],
4593
+ toolName: toolName2,
4594
+ callId: callId2,
4595
+ serviceTag,
4596
+ start: lineStart,
4597
+ end: lineEnd
4598
+ });
4599
+ return;
4600
+ }
4601
+ if (type2 === "function" && isObjectRecord(parsedLine.function)) {
4602
+ const functionRecord = parsedLine.function;
4603
+ const toolName2 = typeof functionRecord.name === "string" ? functionRecord.name.trim() : "";
4604
+ if (!toolName2) return;
4605
+ const callId2 = typeof parsedLine.id === "string" && parsedLine.id.trim().length > 0 ? parsedLine.id.trim() : `tool-call-${index + 1}`;
4606
+ const serviceTag = typeof parsedLine.service === "string" ? parsedLine.service : "";
4607
+ const parsedArgs2 = parseToolArguments((_b = functionRecord.arguments) != null ? _b : {});
4608
+ if (!parsedArgs2) return;
4609
+ requests.push({
4610
+ match: line,
4611
+ groups: [callId2, toolName2, stringifyToolArgs(parsedArgs2), serviceTag],
4612
+ toolName: toolName2,
4613
+ callId: callId2,
4614
+ serviceTag,
4615
+ start: lineStart,
4616
+ end: lineEnd
4617
+ });
4618
+ return;
4619
+ }
4620
+ }
4621
+ } catch (_error) {
4622
+ }
4623
+ const typeMatch = normalized.match(/"type"\s*:\s*"([^"]+)"/i);
4624
+ const type = (_c = typeMatch == null ? void 0 : typeMatch[1]) == null ? void 0 : _c.trim().toLowerCase();
4625
+ if (type !== "tool_use" && type !== "function") return;
4626
+ const idMatch = normalized.match(/"id"\s*:\s*"([^"]+)"/i);
4627
+ const callId = ((_d = idMatch == null ? void 0 : idMatch[1]) == null ? void 0 : _d.trim()) || `tool-call-${index + 1}`;
4628
+ let toolName = "";
4629
+ let argsRaw = "{}";
4630
+ if (type === "tool_use") {
4631
+ const nameMatch = normalized.match(/"name"\s*:\s*"([^"]+)"/i);
4632
+ toolName = ((_e = nameMatch == null ? void 0 : nameMatch[1]) == null ? void 0 : _e.trim()) || "";
4633
+ const inputObject = extractBalancedObjectAfterField(normalized, "input");
4634
+ if (inputObject) {
4635
+ argsRaw = inputObject;
4636
+ }
4637
+ } else {
4638
+ const fnNameMatch = normalized.match(/"function"\s*:\s*\{[\s\S]*?"name"\s*:\s*"([^"]+)"/i);
4639
+ toolName = ((_f = fnNameMatch == null ? void 0 : fnNameMatch[1]) == null ? void 0 : _f.trim()) || "";
4640
+ const argumentsObject = extractBalancedObjectAfterField(normalized, "arguments");
4641
+ if (argumentsObject) {
4642
+ argsRaw = argumentsObject;
4643
+ } else {
4644
+ const argsStringMatch = normalized.match(/"arguments"\s*:\s*"([\s\S]*?)"(?:\s*,|\s*})/i);
4645
+ if (argsStringMatch == null ? void 0 : argsStringMatch[1]) {
4646
+ const rawArgs = argsStringMatch[1];
4647
+ try {
4648
+ argsRaw = JSON.parse(`"${rawArgs}"`);
4649
+ } catch (_error) {
4650
+ argsRaw = rawArgs;
4651
+ }
4652
+ }
4653
+ }
4654
+ }
4655
+ if (!toolName) return;
4656
+ const parsedArgs = parseToolArguments(argsRaw);
4657
+ if (!parsedArgs) return;
4658
+ requests.push({
4659
+ match: line,
4660
+ groups: [callId, toolName, stringifyToolArgs(parsedArgs), ""],
4661
+ toolName,
4662
+ callId,
4663
+ serviceTag: "",
4664
+ start: lineStart,
4665
+ end: lineEnd
4666
+ });
4667
+ });
4668
+ return requests;
4669
+ };
4670
+ var stripStandaloneRawToolJsonLines = (source) => {
4671
+ const text = typeof source === "string" ? source : "";
4672
+ if (!text) return text;
4673
+ const normalizeStandaloneToolJsonLine = (line) => {
4674
+ const trimmed = line.trim();
4675
+ if (!trimmed) return null;
4676
+ let normalized = trimmed;
4677
+ const first = normalized[0];
4678
+ const last = normalized[normalized.length - 1];
4679
+ const hasSymmetricQuote = normalized.length >= 2 && (first === "'" && last === "'" || first === '"' && last === '"' || first === "`" && last === "`");
4680
+ if (hasSymmetricQuote) {
4681
+ const inner = normalized.slice(1, -1).trim();
4682
+ if (first === '"') {
4683
+ try {
4684
+ const decoded = JSON.parse(normalized);
4685
+ if (typeof decoded === "string") {
4686
+ normalized = decoded.trim();
4687
+ } else {
4688
+ normalized = inner;
4689
+ }
4690
+ } catch (_error) {
4691
+ normalized = inner;
4692
+ }
4693
+ } else {
4694
+ normalized = inner;
4695
+ }
4696
+ }
4697
+ const extractedCandidate = extractBalancedObjectCandidate(normalized);
4698
+ if (extractedCandidate) {
4699
+ normalized = extractedCandidate.trim();
4700
+ }
4701
+ if (!normalized.startsWith("{") || !normalized.endsWith("}")) return null;
4702
+ return normalized;
4703
+ };
4704
+ const lines = text.split("\n");
4705
+ let removedAny = false;
4706
+ const kept = lines.filter((line) => {
4707
+ const normalized = normalizeStandaloneToolJsonLine(line);
4708
+ if (!normalized) return true;
4709
+ const hasToolType = /"type"\s*:\s*"(tool_use|function)"/i.test(normalized);
4710
+ if (!hasToolType) return true;
4711
+ const hasName = /"name"\s*:\s*"[^"]+"/i.test(normalized);
4712
+ const hasFunctionName = /"function"\s*:\s*\{[\s\S]*?"name"\s*:\s*"[^"]+"/i.test(normalized);
4713
+ if (!hasName && !hasFunctionName) return true;
4714
+ removedAny = true;
4715
+ return false;
4716
+ });
4717
+ if (!removedAny) return text;
4718
+ return kept.join("\n").replace(/\n{3,}/g, "\n\n");
4719
+ };
4720
+ var extractToolRequestMatchesFromText = (rawResponse) => {
4721
+ const requests = [];
4722
+ const segments = extractTopLevelJsonObjectSegments(rawResponse);
4723
+ segments.forEach((segment, segmentIndex) => {
4724
+ const parsed = resolveToolRequestFromSegment(segment, segmentIndex);
4725
+ if (!parsed) return;
4726
+ requests.push(parsed);
4727
+ });
4728
+ const fallbackRequests = extractLineLevelFallbackToolRequests(rawResponse, requests);
4729
+ fallbackRequests.forEach((request) => {
4730
+ requests.push(request);
4731
+ });
4732
+ if (requests.length === 0) return [];
4733
+ requests.sort((a, b) => a.start - b.start);
4734
+ const seenSignatures = /* @__PURE__ */ new Set();
4735
+ const deduped = requests.filter((request) => {
4736
+ const signature = `${String(request.toolName || "").trim().toLowerCase()}::${String(
4737
+ request.callId || ""
4738
+ ).trim().toLowerCase()}::${request.start}`;
4739
+ if (seenSignatures.has(signature)) return false;
4740
+ seenSignatures.add(signature);
4741
+ return true;
4742
+ });
4743
+ deduped.sort((a, b) => a.start - b.start);
4744
+ return deduped;
4745
+ };
4746
+ var hashInlineMarkerValue = (value) => {
4747
+ let hash = 5381;
4748
+ const input = String(value || "");
4749
+ for (let index = 0; index < input.length; index += 1) {
4750
+ hash = (hash << 5) + hash ^ input.charCodeAt(index);
4751
+ }
4752
+ return (hash >>> 0).toString(36);
4753
+ };
4754
+ var getThinkingBlockSignature = (type, content) => {
4755
+ const normalizedContent = String(content || "").trim();
4756
+ return `${type}-${hashInlineMarkerValue(normalizedContent)}-${normalizedContent.length}`;
4757
+ };
4758
+ var buildThinkingBlockMarker = (type, signature) => {
4759
+ const normalizedType = String(type || "thinking").trim();
4760
+ const normalizedSignature = String(signature || "").trim() || `${normalizedType}-block`;
4761
+ return `${INLINE_THINKING_MARKER_PREFIX}${encodeURIComponent(normalizedType)}|${encodeURIComponent(
4762
+ normalizedSignature
4763
+ )}${INLINE_THINKING_MARKER_SUFFIX}`;
4764
+ };
4765
+ var buildInlineToolMarker = (toolName, callId) => {
4766
+ const normalizedToolName = String(toolName || "").trim() || "tool";
4767
+ const normalizedCallId = String(callId || "").trim() || `${normalizedToolName}-call`;
4768
+ return `${INLINE_TOOL_MARKER_PREFIX}${encodeURIComponent(normalizedToolName)}|${encodeURIComponent(
4769
+ normalizedCallId
4770
+ )}${INLINE_TOOL_MARKER_SUFFIX}`;
4771
+ };
4772
+ var parseInlineToolMarkers = (text) => {
4773
+ const source = typeof text === "string" ? text : "";
4774
+ if (!source) return { parts: [""], markers: [] };
4775
+ const parts = [];
4776
+ const markers = [];
4777
+ let lastIndex = 0;
4778
+ INLINE_TOOL_MARKER_REGEX.lastIndex = 0;
4779
+ let match;
4780
+ while ((match = INLINE_TOOL_MARKER_REGEX.exec(source)) !== null) {
4781
+ parts.push(source.slice(lastIndex, match.index));
4782
+ const encodedToolName = match[1] || "";
4783
+ const encodedCallId = match[2] || "";
4784
+ let toolName = encodedToolName;
4785
+ let callId = encodedCallId;
4786
+ try {
4787
+ toolName = decodeURIComponent(encodedToolName);
4788
+ } catch (_error) {
4789
+ toolName = encodedToolName;
4790
+ }
4791
+ try {
4792
+ callId = decodeURIComponent(encodedCallId);
4793
+ } catch (_error) {
4794
+ callId = encodedCallId;
4795
+ }
4796
+ markers.push({
4797
+ toolName: String(toolName || "").trim() || "tool",
4798
+ callId: String(callId || "").trim() || "call"
4799
+ });
4800
+ lastIndex = match.index + match[0].length;
4801
+ }
4802
+ parts.push(source.slice(lastIndex));
4803
+ return { parts, markers };
4804
+ };
4805
+ var parseInlineThinkingMarkers = (text) => {
4806
+ const source = typeof text === "string" ? text : "";
4807
+ if (!source) return { parts: [""], markers: [] };
4808
+ const parts = [];
4809
+ const markers = [];
4810
+ let lastIndex = 0;
4811
+ INLINE_THINKING_MARKER_REGEX.lastIndex = 0;
4812
+ let match;
4813
+ while ((match = INLINE_THINKING_MARKER_REGEX.exec(source)) !== null) {
4814
+ parts.push(source.slice(lastIndex, match.index));
4815
+ const encodedType = match[1] || "";
4816
+ const encodedSignature = match[2] || "";
4817
+ let rawType = encodedType;
4818
+ let signature = encodedSignature;
4819
+ try {
4820
+ rawType = decodeURIComponent(encodedType);
4821
+ } catch (_error) {
4822
+ rawType = encodedType;
4823
+ }
4824
+ try {
4825
+ signature = decodeURIComponent(encodedSignature);
4826
+ } catch (_error) {
4827
+ signature = encodedSignature;
4828
+ }
4829
+ const normalizedType = String(rawType || "").trim().toLowerCase();
4830
+ const type = normalizedType === "reasoning" ? "reasoning" : normalizedType === "searching" ? "searching" : "thinking";
4831
+ markers.push({
4832
+ type,
4833
+ signature: String(signature || "").trim()
4834
+ });
4835
+ lastIndex = match.index + match[0].length;
4836
+ }
4837
+ parts.push(source.slice(lastIndex));
4838
+ return { parts, markers };
4839
+ };
3736
4840
  var ArrowUpIcon = () => /* @__PURE__ */ React14.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__ */ React14.createElement("path", { d: "M12 19V5M5 12l7-7 7 7" }));
3737
4841
  var StopIcon = () => /* @__PURE__ */ React14.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", className: "ai-chat-icon" }, /* @__PURE__ */ React14.createElement("rect", { x: "6", y: "6", width: "12", height: "12", rx: "2" }));
3738
4842
  var ChevronDownIcon = () => /* @__PURE__ */ React14.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__ */ React14.createElement("path", { d: "m6 9 6 6 6-6" }));
3739
4843
  var ChevronUpIcon = () => /* @__PURE__ */ React14.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__ */ React14.createElement("path", { d: "m18 15-6-6-6 6" }));
3740
4844
  var AgentIcon = () => /* @__PURE__ */ React14.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__ */ React14.createElement("path", { d: "M12 8V4H8" }), /* @__PURE__ */ React14.createElement("rect", { width: "16", height: "12", x: "4", y: "8", rx: "2" }), /* @__PURE__ */ React14.createElement("path", { d: "M2 14h2" }), /* @__PURE__ */ React14.createElement("path", { d: "M20 14h2" }), /* @__PURE__ */ React14.createElement("path", { d: "M15 13v2" }), /* @__PURE__ */ React14.createElement("path", { d: "M9 13v2" }));
4845
+ var ToolIcon = () => /* @__PURE__ */ React14.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__ */ React14.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" }));
3741
4846
  var CheckIcon = () => /* @__PURE__ */ React14.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__ */ React14.createElement("polyline", { points: "20 6 9 17 4 12" }));
3742
4847
  var LLMAsAServiceLogo = () => /* @__PURE__ */ React14.createElement("svg", { width: "16", height: "16", viewBox: "0 0 72 72", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React14.createElement("ellipse", { cx: "14.0868", cy: "59.2146", rx: "7.8261", ry: "7.7854", fill: "#2487D8" }), /* @__PURE__ */ React14.createElement("ellipse", { cx: "24.9013", cy: "43.0776", rx: "6.11858", ry: "6.08676", fill: "#2487D8" }), /* @__PURE__ */ React14.createElement("ellipse", { cx: "45.391", cy: "43.0776", rx: "6.11858", ry: "6.08676", fill: "#2487D8" }), /* @__PURE__ */ React14.createElement("ellipse", { cx: "65.8813", cy: "43.0776", rx: "6.11858", ry: "6.08676", fill: "#2487D8" }), /* @__PURE__ */ React14.createElement("ellipse", { cx: "35.1461", cy: "26.5327", rx: "4.41103", ry: "4.3878", fill: "#2487D8" }), /* @__PURE__ */ React14.createElement("ellipse", { cx: "55.6364", cy: "26.5327", rx: "4.41103", ry: "4.3878", fill: "#2487D8" }), /* @__PURE__ */ React14.createElement("ellipse", { cx: "45.391", cy: "10.3959", rx: "2.70351", ry: "2.68919", fill: "#2487D8" }));
3743
4848
  var AlertCircleIcon = () => /* @__PURE__ */ React14.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__ */ React14.createElement("circle", { cx: "12", cy: "12", r: "10" }), /* @__PURE__ */ React14.createElement("line", { x1: "12", x2: "12", y1: "8", y2: "12" }), /* @__PURE__ */ React14.createElement("line", { x1: "12", x2: "12.01", y1: "16", y2: "16" }));
3744
4849
  var CloseIcon = () => /* @__PURE__ */ React14.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__ */ React14.createElement("line", { x1: "18", x2: "6", y1: "6", y2: "18" }), /* @__PURE__ */ React14.createElement("line", { x1: "6", x2: "18", y1: "6", y2: "18" }));
3745
4850
  var ChatInput = React14.memo(({
3746
4851
  placeholder,
3747
- idle,
4852
+ isBusy,
3748
4853
  onSubmit,
3749
4854
  onStop,
3750
4855
  agentOptions,
@@ -3777,14 +4882,14 @@ var ChatInput = React14.memo(({
3777
4882
  }, []);
3778
4883
  const handleSubmit = useCallback2(() => {
3779
4884
  const trimmed = inputValue.trim();
3780
- if (trimmed && idle) {
4885
+ if (trimmed && !isBusy) {
3781
4886
  onSubmit(trimmed);
3782
4887
  setInputValue("");
3783
4888
  if (textareaRef.current) {
3784
4889
  textareaRef.current.style.height = "auto";
3785
4890
  }
3786
4891
  }
3787
- }, [inputValue, idle, onSubmit]);
4892
+ }, [inputValue, isBusy, onSubmit]);
3788
4893
  useEffect8(() => {
3789
4894
  const handleClickOutside = (event) => {
3790
4895
  if (containerRef.current && !containerRef.current.contains(event.target)) {
@@ -3820,9 +4925,6 @@ var ChatInput = React14.memo(({
3820
4925
  const hasRawData2 = (section) => {
3821
4926
  return typeof section.rawData === "string";
3822
4927
  };
3823
- const hasStructuredData = (section) => {
3824
- return typeof section.data === "object" && section.data !== null;
3825
- };
3826
4928
  const estimateSectionTokens2 = (section) => {
3827
4929
  var _a;
3828
4930
  if (typeof section.tokens === "number") {
@@ -3833,32 +4935,6 @@ var ChatInput = React14.memo(({
3833
4935
  }
3834
4936
  return Math.ceil(JSON.stringify((_a = section.data) != null ? _a : {}).length / 4);
3835
4937
  };
3836
- const detectFormat = (section) => {
3837
- if (section.format) {
3838
- return section.format;
3839
- }
3840
- if (!hasRawData2(section)) {
3841
- return "json";
3842
- }
3843
- const raw = section.rawData.trim();
3844
- if (raw.startsWith("{") || raw.startsWith("[")) return "json";
3845
- if (raw.includes("# ") || raw.includes("```")) return "markdown";
3846
- return "text";
3847
- };
3848
- const formatBadge = (format) => {
3849
- if (format === "toon") return "TOON";
3850
- if (format === "markdown") return "MD";
3851
- if (format === "text") return "TEXT";
3852
- return "JSON";
3853
- };
3854
- const [detailViewModeBySection, setDetailViewModeBySection] = useState7({});
3855
- const getVisibleDetailMode = (section) => {
3856
- var _a;
3857
- if (hasRawData2(section)) {
3858
- return (_a = detailViewModeBySection[section.id]) != null ? _a : "raw";
3859
- }
3860
- return "structured";
3861
- };
3862
4938
  const getStructuredContent = (section) => {
3863
4939
  var _a;
3864
4940
  return JSON.stringify((_a = section.data) != null ? _a : {}, null, 2);
@@ -3950,10 +5026,10 @@ var ChatInput = React14.memo(({
3950
5026
  className: `ai-chat-context-popover__progress-bar ${isOverLimit ? "ai-chat-context-popover__progress-bar--warning" : ""}`,
3951
5027
  style: { width: `${Math.min(tokenPercentage, 100)}%` }
3952
5028
  }
3953
- ))), /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-context-popover__sections" }, contextSections.map((section) => /* @__PURE__ */ React14.createElement(
5029
+ ))), /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-context-popover__sections" }, contextSections.map((section, index) => /* @__PURE__ */ React14.createElement(
3954
5030
  "div",
3955
5031
  {
3956
- key: section.id,
5032
+ key: `${section.id}-${index}`,
3957
5033
  className: `ai-chat-context-popover__section-item ${enableContextDetailView ? "ai-chat-context-popover__section-item--clickable" : ""}`,
3958
5034
  onClick: () => {
3959
5035
  if (enableContextDetailView) {
@@ -3964,7 +5040,6 @@ var ChatInput = React14.memo(({
3964
5040
  },
3965
5041
  /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-context-popover__section-icon" }, "\u{1F4C4}"),
3966
5042
  /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-context-popover__section-title" }, section.title),
3967
- /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-context-popover__section-format" }, formatBadge(detectFormat(section))),
3968
5043
  /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-context-popover__section-tokens" }, estimateSectionTokens2(section))
3969
5044
  ))), enableContextDetailView && /* @__PURE__ */ React14.createElement(
3970
5045
  "button",
@@ -4006,15 +5081,13 @@ var ChatInput = React14.memo(({
4006
5081
  className: `ai-chat-context-popover__progress-bar ${isOverLimit ? "ai-chat-context-popover__progress-bar--warning" : ""}`,
4007
5082
  style: { width: `${Math.min(tokenPercentage, 100)}%` }
4008
5083
  }
4009
- ))), /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-context-popover__detail-sections" }, contextSections.map((section) => {
5084
+ ))), /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-context-popover__detail-sections" }, contextSections.map((section, index) => {
4010
5085
  const isRawSection = hasRawData2(section);
4011
- const hasBothViews = isRawSection && hasStructuredData(section);
4012
- const visibleMode = getVisibleDetailMode(section);
4013
5086
  const isEnabled = !disabledSectionIds.has(section.id);
4014
5087
  return /* @__PURE__ */ React14.createElement(
4015
5088
  "details",
4016
5089
  {
4017
- key: section.id,
5090
+ key: `${section.id}-${index}`,
4018
5091
  className: `ai-chat-context-popover__detail-section ${!isEnabled ? "ai-chat-context-popover__detail-section--disabled" : ""}`,
4019
5092
  open: expandedSectionId === section.id
4020
5093
  },
@@ -4040,43 +5113,18 @@ var ChatInput = React14.memo(({
4040
5113
  }
4041
5114
  ),
4042
5115
  /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-context-toggle__slider" })
4043
- )), /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-context-popover__detail-section-meta" }, /* @__PURE__ */ React14.createElement("code", null, `{{${section.id}}}`), /* @__PURE__ */ React14.createElement("span", null, formatBadge(detectFormat(section))), /* @__PURE__ */ React14.createElement("span", null, "~", estimateSectionTokens2(section)))),
4044
- hasBothViews && /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-context-popover__detail-view-toggle" }, /* @__PURE__ */ React14.createElement(
4045
- "button",
4046
- {
4047
- type: "button",
4048
- className: `ai-chat-context-popover__detail-view-button ${visibleMode === "raw" ? "active" : ""}`,
4049
- onClick: (e) => {
4050
- e.preventDefault();
4051
- e.stopPropagation();
4052
- setDetailViewModeBySection((prev) => __spreadProps(__spreadValues({}, prev), { [section.id]: "raw" }));
4053
- }
4054
- },
4055
- "Raw"
4056
- ), /* @__PURE__ */ React14.createElement(
4057
- "button",
4058
- {
4059
- type: "button",
4060
- className: `ai-chat-context-popover__detail-view-button ${visibleMode === "structured" ? "active" : ""}`,
4061
- onClick: (e) => {
4062
- e.preventDefault();
4063
- e.stopPropagation();
4064
- setDetailViewModeBySection((prev) => __spreadProps(__spreadValues({}, prev), { [section.id]: "structured" }));
4065
- }
4066
- },
4067
- "Structured"
4068
- )),
4069
- /* @__PURE__ */ React14.createElement("pre", { className: "ai-chat-context-popover__detail-content" }, /* @__PURE__ */ React14.createElement("code", null, visibleMode === "raw" && isRawSection ? section.rawData : getStructuredContent(section)))
5116
+ )), /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-context-popover__detail-section-meta" }, /* @__PURE__ */ React14.createElement("code", null, `{{${section.id}}}`), /* @__PURE__ */ React14.createElement("span", null, "~", estimateSectionTokens2(section)))),
5117
+ /* @__PURE__ */ React14.createElement("pre", { className: "ai-chat-context-popover__detail-content" }, /* @__PURE__ */ React14.createElement("code", null, isRawSection ? section.rawData : getStructuredContent(section)))
4070
5118
  );
4071
5119
  })))
4072
5120
  )), /* @__PURE__ */ React14.createElement(
4073
5121
  "button",
4074
5122
  {
4075
- className: `ai-chat-send-button ${idle && !inputValue.trim() ? "ai-chat-send-button--disabled" : ""} ${!idle ? "ai-chat-send-button--stop" : ""}`,
4076
- onClick: () => idle ? handleSubmit() : onStop(),
4077
- disabled: idle && !inputValue.trim()
5123
+ className: `ai-chat-send-button ${!isBusy && !inputValue.trim() ? "ai-chat-send-button--disabled" : ""} ${isBusy ? "ai-chat-send-button--stop" : ""}`,
5124
+ onClick: () => isBusy ? onStop() : handleSubmit(),
5125
+ disabled: !isBusy && !inputValue.trim()
4078
5126
  },
4079
- idle ? /* @__PURE__ */ React14.createElement(ArrowUpIcon, null) : /* @__PURE__ */ React14.createElement(StopIcon, null)
5127
+ isBusy ? /* @__PURE__ */ React14.createElement(StopIcon, null) : /* @__PURE__ */ React14.createElement(ArrowUpIcon, null)
4080
5128
  )),
4081
5129
  agentOptions.length > 0 && dropdownOpen && /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-agent-selector__dropdown-inside" }, agentOptions.map((option) => /* @__PURE__ */ React14.createElement(
4082
5130
  "button",
@@ -4133,7 +5181,11 @@ var AIChatPanel = ({
4133
5181
  initialHistory = {},
4134
5182
  hideRagContextInPrompt = true,
4135
5183
  createConversationOnFirstChat = true,
5184
+ autoApproveTools = false,
4136
5185
  mcpServers = [],
5186
+ resolveMcpAuthHeaders,
5187
+ localToolExecutors,
5188
+ traceContextMode = "standard",
4137
5189
  progressiveActions = true,
4138
5190
  agentOptions = [],
4139
5191
  currentAgentId,
@@ -4146,6 +5198,7 @@ var AIChatPanel = ({
4146
5198
  disabledSectionIds: propDisabledSectionIds,
4147
5199
  onToggleSection: propOnToggleSection,
4148
5200
  onConversationCreated,
5201
+ onBeforeSend,
4149
5202
  // UI Customization Props
4150
5203
  cssUrl,
4151
5204
  markdownClass,
@@ -4181,6 +5234,8 @@ var AIChatPanel = ({
4181
5234
  const [collapsedBlocks, setCollapsedBlocks] = useState7(/* @__PURE__ */ new Set());
4182
5235
  const hasAutoCollapsedRef = useRef6(false);
4183
5236
  const prevBlockCountRef = useRef6(0);
5237
+ const thinkingBlocksByKeyRef = useRef6({});
5238
+ const [thinkingBlocksByKey, setThinkingBlocksByKey] = useState7({});
4184
5239
  const [newConversationConfirm, setNewConversationConfirm] = useState7(false);
4185
5240
  const [justReset, setJustReset] = useState7(false);
4186
5241
  const [copiedCallId, setCopiedCallId] = useState7(null);
@@ -4200,8 +5255,64 @@ var AIChatPanel = ({
4200
5255
  const [pendingToolRequests, setPendingToolRequests] = useState7([]);
4201
5256
  const [sessionApprovedTools, setSessionApprovedTools] = useState7([]);
4202
5257
  const [alwaysApprovedTools, setAlwaysApprovedTools] = useState7([]);
5258
+ const [toolList, setToolList] = useState7([]);
5259
+ const [toolsLoading, setToolsLoading] = useState7(false);
5260
+ const [toolsFetchError, setToolsFetchError] = useState7(false);
5261
+ const [resolvedMcpServers, setResolvedMcpServers] = useState7(mcpServers || []);
5262
+ const [activeToolCalls, setActiveToolCalls] = useState7([]);
5263
+ const normalizeToolName = useCallback2((toolName) => {
5264
+ return String(toolName != null ? toolName : "").trim().toLowerCase();
5265
+ }, []);
5266
+ const getToolCallSignature = useCallback2(
5267
+ (toolName, callId) => {
5268
+ const normalizedToolName = normalizeToolName(toolName);
5269
+ const normalizedCallId = String(callId != null ? callId : "").trim();
5270
+ if (!normalizedToolName || !normalizedCallId) return "";
5271
+ return `${normalizedToolName}::${normalizedCallId}`;
5272
+ },
5273
+ [normalizeToolName]
5274
+ );
5275
+ const alwaysApprovedToolsStorageKey = useMemo2(() => {
5276
+ const customerId = (customer == null ? void 0 : customer.customer_id) || (customer == null ? void 0 : customer.id) || (customer == null ? void 0 : customer.customer_user_email) || "anonymous";
5277
+ const agentIdForScope = currentAgentId || agent || "default";
5278
+ return `llmasaservice-ui:always-approved-tools:${project_id}:${customerId}:${agentIdForScope}`;
5279
+ }, [project_id, customer, currentAgentId, agent]);
4203
5280
  const [internalDisabledSectionIds, setInternalDisabledSectionIds] = useState7(/* @__PURE__ */ new Set());
4204
5281
  const disabledSectionIds = propDisabledSectionIds != null ? propDisabledSectionIds : internalDisabledSectionIds;
5282
+ useEffect8(() => {
5283
+ if (typeof window === "undefined") return;
5284
+ try {
5285
+ const stored = window.localStorage.getItem(alwaysApprovedToolsStorageKey);
5286
+ if (!stored) return;
5287
+ const parsed = JSON.parse(stored);
5288
+ if (!Array.isArray(parsed)) return;
5289
+ const normalized = Array.from(
5290
+ new Set(
5291
+ parsed.map((value) => normalizeToolName(String(value))).filter((value) => value.length > 0)
5292
+ )
5293
+ );
5294
+ setAlwaysApprovedTools(normalized);
5295
+ } catch (error2) {
5296
+ console.warn("[AIChatPanel] Failed to load always-approved tools from localStorage:", error2);
5297
+ }
5298
+ }, [alwaysApprovedToolsStorageKey, normalizeToolName]);
5299
+ useEffect8(() => {
5300
+ if (typeof window === "undefined") return;
5301
+ try {
5302
+ if (alwaysApprovedTools.length === 0) {
5303
+ window.localStorage.removeItem(alwaysApprovedToolsStorageKey);
5304
+ return;
5305
+ }
5306
+ window.localStorage.setItem(
5307
+ alwaysApprovedToolsStorageKey,
5308
+ JSON.stringify(
5309
+ Array.from(new Set(alwaysApprovedTools.map((value) => normalizeToolName(value)).filter(Boolean)))
5310
+ )
5311
+ );
5312
+ } catch (error2) {
5313
+ console.warn("[AIChatPanel] Failed to persist always-approved tools to localStorage:", error2);
5314
+ }
5315
+ }, [alwaysApprovedToolsStorageKey, alwaysApprovedTools, normalizeToolName]);
4205
5316
  useEffect8(() => {
4206
5317
  setShowEmailPanel(customerEmailCaptureMode !== "HIDE");
4207
5318
  if (customerEmailCaptureMode === "REQUIRED") {
@@ -4224,6 +5335,14 @@ var AIChatPanel = ({
4224
5335
  const latestHistoryRef = useRef6(initialHistory);
4225
5336
  const initialPromptSentRef = useRef6(false);
4226
5337
  const lastFollowOnPromptRef = useRef6("");
5338
+ const handledToolCallSignaturesRef = useRef6(/* @__PURE__ */ new Set());
5339
+ const inFlightToolCallSignaturesRef = useRef6(/* @__PURE__ */ new Set());
5340
+ const toolContinuationCountRef = useRef6(0);
5341
+ const activeStreamAppendBaseRef = useRef6(null);
5342
+ const toolRequestProcessingRef = useRef6(false);
5343
+ const queuedToolRequestsRef = useRef6(null);
5344
+ const suppressAbortHistoryUpdateRef = useRef6(false);
5345
+ const toolReplaySummariesByKeyRef = useRef6({});
4227
5346
  useEffect8(() => {
4228
5347
  if (!initialHistory) return;
4229
5348
  setHistory((prev) => {
@@ -4242,10 +5361,158 @@ var AIChatPanel = ({
4242
5361
  return prev;
4243
5362
  });
4244
5363
  }, [initialHistory]);
4245
- const llmResult = useLLM2(__spreadValues(__spreadValues(__spreadValues(__spreadValues({
5364
+ useEffect8(() => {
5365
+ latestHistoryRef.current = history;
5366
+ }, [history]);
5367
+ useEffect8(() => {
5368
+ let cancelled = false;
5369
+ const resolveServers = () => __async(void 0, null, function* () {
5370
+ if (!mcpServers || mcpServers.length === 0) {
5371
+ if (!cancelled) {
5372
+ setResolvedMcpServers((prev) => prev.length === 0 ? prev : []);
5373
+ }
5374
+ return;
5375
+ }
5376
+ if (!resolveMcpAuthHeaders) {
5377
+ if (!cancelled) {
5378
+ setResolvedMcpServers((prev) => {
5379
+ const hasSameServers = prev.length === mcpServers.length && prev.every((server, index) => server === mcpServers[index]);
5380
+ return hasSameServers ? prev : mcpServers;
5381
+ });
5382
+ }
5383
+ return;
5384
+ }
5385
+ try {
5386
+ const enriched = yield Promise.all(
5387
+ mcpServers.map((server) => __async(void 0, null, function* () {
5388
+ const resolved = yield resolveMcpAuthHeaders({
5389
+ phase: "list",
5390
+ mcpServer: server || {},
5391
+ projectId: project_id,
5392
+ customer
5393
+ });
5394
+ return __spreadProps(__spreadValues({}, server || {}), {
5395
+ headers: __spreadValues(__spreadValues({}, typeof (server == null ? void 0 : server.headers) === "object" && (server == null ? void 0 : server.headers) ? server.headers : {}), normalizeMcpHeaders(
5396
+ resolved
5397
+ ))
5398
+ });
5399
+ }))
5400
+ );
5401
+ if (!cancelled) setResolvedMcpServers(enriched);
5402
+ } catch (error2) {
5403
+ console.error("[AIChatPanel] Failed to resolve MCP auth headers:", error2);
5404
+ if (!cancelled) setResolvedMcpServers(mcpServers);
5405
+ }
5406
+ });
5407
+ void resolveServers();
5408
+ return () => {
5409
+ cancelled = true;
5410
+ };
5411
+ }, [mcpServers, resolveMcpAuthHeaders, project_id, customer]);
5412
+ const buildMcpRequestHeaders = useCallback2(
5413
+ (_0) => __async(void 0, [_0], function* ({
5414
+ phase,
5415
+ mcpServer,
5416
+ toolName,
5417
+ toolArgs
5418
+ }) {
5419
+ const merged = {};
5420
+ const packScopeIntoAccessToken = (headers) => {
5421
+ const accessToken = typeof headers["x-mcp-access-token"] === "string" ? headers["x-mcp-access-token"].trim() : "";
5422
+ const scopeToken = typeof headers["x-client-id"] === "string" ? headers["x-client-id"].trim() : "";
5423
+ if (!accessToken || !scopeToken) return headers;
5424
+ return __spreadProps(__spreadValues({}, headers), {
5425
+ "x-mcp-access-token": `${accessToken}::scope::${scopeToken}`
5426
+ });
5427
+ };
5428
+ const baseAccessToken = typeof mcpServer.accessToken === "string" ? mcpServer.accessToken.trim() : "";
5429
+ if (baseAccessToken) {
5430
+ merged["x-mcp-access-token"] = baseAccessToken;
5431
+ }
5432
+ if (project_id) {
5433
+ merged["x-project-id"] = project_id;
5434
+ }
5435
+ if (!resolveMcpAuthHeaders) return merged;
5436
+ try {
5437
+ const resolved = yield resolveMcpAuthHeaders({
5438
+ phase,
5439
+ mcpServer,
5440
+ projectId: project_id,
5441
+ customer,
5442
+ toolName,
5443
+ toolArgs
5444
+ });
5445
+ return packScopeIntoAccessToken(__spreadValues(__spreadValues({}, merged), normalizeMcpHeaders(
5446
+ resolved
5447
+ )));
5448
+ } catch (error2) {
5449
+ console.error(
5450
+ `Failed to resolve MCP auth headers for ${phase} request:`,
5451
+ error2
5452
+ );
5453
+ return merged;
5454
+ }
5455
+ }),
5456
+ [project_id, customer, resolveMcpAuthHeaders]
5457
+ );
5458
+ useEffect8(() => {
5459
+ const fetchAndSetTools = () => __async(void 0, null, function* () {
5460
+ if (!resolvedMcpServers || resolvedMcpServers.length === 0) {
5461
+ setToolList((prev) => prev.length === 0 ? prev : []);
5462
+ setToolsLoading((prev) => prev ? false : prev);
5463
+ setToolsFetchError((prev) => prev ? false : prev);
5464
+ return;
5465
+ }
5466
+ setToolsLoading(true);
5467
+ setToolsFetchError(false);
5468
+ try {
5469
+ const fetchPromises = (resolvedMcpServers != null ? resolvedMcpServers : []).map((m) => __async(void 0, null, function* () {
5470
+ const urlToFetch = `${publicAPIUrl}/tools/${encodeURIComponent(m.url)}`;
5471
+ const requestHeaders = yield buildMcpRequestHeaders({
5472
+ phase: "list",
5473
+ mcpServer: m
5474
+ });
5475
+ const response2 = yield fetch(urlToFetch, {
5476
+ headers: requestHeaders
5477
+ });
5478
+ if (!response2.ok) {
5479
+ const errorBody = yield response2.text();
5480
+ throw new Error(
5481
+ `HTTP ${response2.status}: ${response2.statusText} ${errorBody}`
5482
+ );
5483
+ }
5484
+ const toolsFromServer = yield response2.json();
5485
+ if (!Array.isArray(toolsFromServer)) return [];
5486
+ return toolsFromServer.map((tool) => __spreadProps(__spreadValues({}, tool), {
5487
+ url: m.url,
5488
+ accessToken: m.accessToken || "",
5489
+ headers: requestHeaders
5490
+ }));
5491
+ }));
5492
+ const results = yield Promise.all(fetchPromises);
5493
+ const allTools = results.flat();
5494
+ setToolList(allTools);
5495
+ setToolsFetchError(false);
5496
+ } catch (error2) {
5497
+ console.error("[AIChatPanel] Failed to load MCP tools:", error2);
5498
+ setToolList([]);
5499
+ setToolsFetchError(true);
5500
+ } finally {
5501
+ setToolsLoading(false);
5502
+ }
5503
+ });
5504
+ void fetchAndSetTools();
5505
+ }, [resolvedMcpServers, publicAPIUrl, buildMcpRequestHeaders]);
5506
+ const llmResult = useLLM2(__spreadProps(__spreadValues(__spreadValues(__spreadValues({
4246
5507
  project_id,
4247
5508
  customer
4248
- }, url && { url }), service && { group_id: service }), agent && { agent }), mcpServers && mcpServers.length > 0 && { mcp_servers: mcpServers }));
5509
+ }, url && { url }), service && { group_id: service }), agent && { agent }), {
5510
+ tools: toolList.map((item) => ({
5511
+ name: item.name,
5512
+ description: item.description,
5513
+ parameters: item.parameters
5514
+ }))
5515
+ }));
4249
5516
  const {
4250
5517
  send,
4251
5518
  response,
@@ -4255,9 +5522,6 @@ var AIChatPanel = ({
4255
5522
  setResponse,
4256
5523
  error: llmError
4257
5524
  } = llmResult;
4258
- const toolList = llmResult.toolList || [];
4259
- const toolsLoading = llmResult.toolsLoading || false;
4260
- const toolsFetchError = llmResult.toolsFetchError || null;
4261
5525
  const historyCallbackRef = useRef6(historyChangedCallback);
4262
5526
  const responseCompleteCallbackRef = useRef6(responseCompleteCallback);
4263
5527
  const responseRef = useRef6(response);
@@ -4298,18 +5562,26 @@ var AIChatPanel = ({
4298
5562
  }
4299
5563
  }, [propOnToggleSection]);
4300
5564
  const ensureConversation = useCallback2(() => {
4301
- var _a2, _b;
5565
+ const normalizedConversationId = typeof currentConversation === "string" ? currentConversation.trim() : "";
4302
5566
  console.log("ensureConversation - called with:", {
4303
- currentConversation,
5567
+ currentConversation: normalizedConversationId || null,
4304
5568
  createConversationOnFirstChat,
4305
5569
  project_id,
4306
5570
  publicAPIUrl
4307
5571
  });
4308
- if ((!currentConversation || currentConversation === "") && createConversationOnFirstChat) {
4309
- if (!project_id) {
4310
- console.error("ensureConversation - Cannot create conversation without project_id");
4311
- return Promise.resolve("");
4312
- }
5572
+ if (normalizedConversationId) {
5573
+ console.log("ensureConversation - using existing conversation:", normalizedConversationId);
5574
+ return Promise.resolve(normalizedConversationId);
5575
+ }
5576
+ if (!createConversationOnFirstChat) {
5577
+ return Promise.resolve("");
5578
+ }
5579
+ if (!project_id) {
5580
+ console.error("ensureConversation - Cannot create conversation without project_id");
5581
+ return Promise.resolve("");
5582
+ }
5583
+ const createConversation = () => {
5584
+ var _a2, _b;
4313
5585
  const requestBody = {
4314
5586
  project_id,
4315
5587
  agentId: agent,
@@ -4335,11 +5607,13 @@ var AIChatPanel = ({
4335
5607
  }
4336
5608
  return res.json();
4337
5609
  })).then((newConvo) => {
5610
+ var _a3;
4338
5611
  console.log("ensureConversation - API response:", newConvo);
4339
- if (newConvo == null ? void 0 : newConvo.id) {
4340
- console.log("ensureConversation - New conversation ID:", newConvo.id);
4341
- setCurrentConversation(newConvo.id);
4342
- return newConvo.id;
5612
+ 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() || "";
5613
+ if (createdId) {
5614
+ console.log("ensureConversation - New conversation ID:", createdId);
5615
+ setCurrentConversation(createdId);
5616
+ return createdId;
4343
5617
  }
4344
5618
  console.warn("ensureConversation - No ID in response");
4345
5619
  return "";
@@ -4347,9 +5621,8 @@ var AIChatPanel = ({
4347
5621
  console.error("Error creating new conversation", error2);
4348
5622
  return "";
4349
5623
  });
4350
- }
4351
- console.log("ensureConversation - using existing conversation:", currentConversation);
4352
- return Promise.resolve(currentConversation);
5624
+ };
5625
+ return createConversation();
4353
5626
  }, [currentConversation, createConversationOnFirstChat, publicAPIUrl, project_id, agent, customer, browserInfo]);
4354
5627
  const dataWithExtras = useCallback2(() => {
4355
5628
  var _a2, _b, _c, _d, _e, _f, _g, _h;
@@ -4532,19 +5805,565 @@ var AIChatPanel = ({
4532
5805
  }
4533
5806
  return false;
4534
5807
  }, [customerEmailCaptureMode, emailInputSet]);
4535
- const handleToolApproval = useCallback2((toolName, scope) => {
4536
- if (scope === "session" || scope === "always") {
4537
- setSessionApprovedTools((prev) => Array.from(/* @__PURE__ */ new Set([...prev, toolName])));
4538
- }
4539
- if (scope === "always") {
4540
- setAlwaysApprovedTools((prev) => Array.from(/* @__PURE__ */ new Set([...prev, toolName])));
5808
+ const pendingToolRequestsRef = useRef6(pendingToolRequests);
5809
+ const streamIdleRef = useRef6(idle);
5810
+ streamIdleRef.current = idle;
5811
+ const waitForStreamIdle = useCallback2((timeoutMs = 2500) => __async(void 0, null, function* () {
5812
+ const startedAt = Date.now();
5813
+ while (!streamIdleRef.current && Date.now() - startedAt < timeoutMs) {
5814
+ yield new Promise((resolve) => {
5815
+ setTimeout(resolve, 25);
5816
+ });
4541
5817
  }
4542
- setPendingToolRequests((prev) => prev.filter((r) => r.toolName !== toolName));
4543
- console.log(`[AIChatPanel] Tool "${toolName}" approved with scope: ${scope}`);
4544
- }, []);
4545
- const getUniqueToolNames = useCallback2(() => {
4546
- return Array.from(new Set(pendingToolRequests.map((r) => r.toolName)));
5818
+ }), []);
5819
+ useEffect8(() => {
5820
+ pendingToolRequestsRef.current = pendingToolRequests;
4547
5821
  }, [pendingToolRequests]);
5822
+ const processGivenToolRequests = useCallback2(
5823
+ (requests) => __async(void 0, null, function* () {
5824
+ var _a2, _b, _c;
5825
+ const dedupeToolRequests = (input) => {
5826
+ const seen = /* @__PURE__ */ new Set();
5827
+ const deduped = [];
5828
+ for (const request of Array.isArray(input) ? input : []) {
5829
+ if (!request) continue;
5830
+ const signature = getToolCallSignature(request.toolName, request.callId) || request.match;
5831
+ if (!signature || seen.has(signature)) continue;
5832
+ seen.add(signature);
5833
+ deduped.push(request);
5834
+ }
5835
+ return deduped;
5836
+ };
5837
+ if (toolRequestProcessingRef.current) {
5838
+ const queued = dedupeToolRequests([
5839
+ ...queuedToolRequestsRef.current || [],
5840
+ ...Array.isArray(requests) ? requests : []
5841
+ ]);
5842
+ if (queued.length > 0) {
5843
+ queuedToolRequestsRef.current = queued;
5844
+ }
5845
+ return;
5846
+ }
5847
+ toolRequestProcessingRef.current = true;
5848
+ try {
5849
+ let requestsToProcess = requests;
5850
+ if (!requestsToProcess || requestsToProcess.length === 0) {
5851
+ requestsToProcess = pendingToolRequestsRef.current || [];
5852
+ }
5853
+ if (!requestsToProcess || requestsToProcess.length === 0) return;
5854
+ setIsLoading(true);
5855
+ const userPrompt = lastPromptRef.current || lastKeyRef.current || "";
5856
+ const lastPromptKey = lastKeyRef.current;
5857
+ const assistantSeedText = lastPromptKey && ((_a2 = latestHistoryRef.current[lastPromptKey]) == null ? void 0 : _a2.content) ? (_b = latestHistoryRef.current[lastPromptKey]) == null ? void 0 : _b.content : "";
5858
+ const historyForContinuation = latestHistoryRef.current || {};
5859
+ const newMessages = [];
5860
+ Object.entries(historyForContinuation).forEach(([historyPrompt, historyEntry]) => {
5861
+ let promptForHistory = String(historyPrompt || "");
5862
+ const isoTimestampRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z:/;
5863
+ if (isoTimestampRegex.test(promptForHistory)) {
5864
+ const colonIndex = promptForHistory.indexOf(":", 19);
5865
+ promptForHistory = promptForHistory.substring(colonIndex + 1);
5866
+ } else if (/^\d+:/.test(promptForHistory)) {
5867
+ const colonIndex = promptForHistory.indexOf(":");
5868
+ promptForHistory = promptForHistory.substring(colonIndex + 1);
5869
+ }
5870
+ const typedHistoryEntry = historyEntry || { content: "", callId: "" };
5871
+ const assistantBaseContent = typeof typedHistoryEntry.content === "string" ? typedHistoryEntry.content : "";
5872
+ let assistantContextContent = assistantBaseContent;
5873
+ if (traceContextMode === "full") {
5874
+ const traceSummary = buildCompactTraceSummary({
5875
+ reasoningBlocks: thinkingBlocksByKeyRef.current[historyPrompt] || [],
5876
+ toolCalls: typedHistoryEntry.toolCalls,
5877
+ toolResponses: typedHistoryEntry.toolResponses
5878
+ });
5879
+ if (traceSummary) {
5880
+ assistantContextContent = assistantBaseContent ? `${assistantBaseContent}
5881
+
5882
+ ${traceSummary}` : traceSummary;
5883
+ }
5884
+ }
5885
+ newMessages.push({
5886
+ role: "user",
5887
+ content: [
5888
+ {
5889
+ type: "text",
5890
+ text: promptForHistory
5891
+ }
5892
+ ]
5893
+ });
5894
+ newMessages.push({
5895
+ role: "assistant",
5896
+ content: [
5897
+ {
5898
+ type: "text",
5899
+ text: assistantContextContent
5900
+ }
5901
+ ]
5902
+ });
5903
+ });
5904
+ if (newMessages.length === 0 && userPrompt.trim().length > 0) {
5905
+ newMessages.push({
5906
+ role: "user",
5907
+ content: [
5908
+ {
5909
+ type: "text",
5910
+ text: userPrompt
5911
+ }
5912
+ ]
5913
+ });
5914
+ if (assistantSeedText.trim().length > 0) {
5915
+ newMessages.push({
5916
+ role: "assistant",
5917
+ content: [
5918
+ {
5919
+ type: "text",
5920
+ text: assistantSeedText
5921
+ }
5922
+ ]
5923
+ });
5924
+ }
5925
+ }
5926
+ const parsedToolCalls = yield Promise.all(
5927
+ requestsToProcess.map((req, index) => __async(void 0, null, function* () {
5928
+ var _a3, _b2, _c2, _d, _e, _f;
5929
+ let parsedToolCall = null;
5930
+ try {
5931
+ parsedToolCall = JSON.parse(req.match);
5932
+ } catch (error2) {
5933
+ console.error("[AIChatPanel] Failed to parse tool call payload:", error2);
5934
+ }
5935
+ 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 : "");
5936
+ if (!toolName) return null;
5937
+ const rawCallId = req.callId || req.groups[0] || (parsedToolCall == null ? void 0 : parsedToolCall.id) || (parsedToolCall == null ? void 0 : parsedToolCall.tool_call_id) || `${toolName}-${index + 1}`;
5938
+ const callId = typeof rawCallId === "string" && rawCallId.trim().length > 0 && rawCallId !== "functionCall" ? rawCallId : `${toolName}-${index + 1}`;
5939
+ let args = {};
5940
+ 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 : "{}";
5941
+ const parsedArgs = parseToolArguments(rawArgs);
5942
+ if (!parsedArgs) {
5943
+ console.error("[AIChatPanel] Failed to parse tool arguments", {
5944
+ toolName,
5945
+ callId,
5946
+ rawArgsPreview: typeof rawArgs === "string" ? rawArgs.slice(0, 500) : JSON.stringify(rawArgs).slice(0, 500)
5947
+ });
5948
+ return null;
5949
+ }
5950
+ args = parsedArgs;
5951
+ 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 || "";
5952
+ const callSignature = getToolCallSignature(toolName, callId);
5953
+ if (!callSignature) return null;
5954
+ return {
5955
+ req,
5956
+ toolName,
5957
+ callId,
5958
+ args,
5959
+ serviceTag,
5960
+ callSignature
5961
+ };
5962
+ }))
5963
+ );
5964
+ const toolCallBatch = parsedToolCalls.filter(Boolean);
5965
+ const seenCallSignatures = /* @__PURE__ */ new Set();
5966
+ const callsToRun = [];
5967
+ toolCallBatch.forEach((toolCall) => {
5968
+ if (seenCallSignatures.has(toolCall.callSignature)) return;
5969
+ seenCallSignatures.add(toolCall.callSignature);
5970
+ if (handledToolCallSignaturesRef.current.has(toolCall.callSignature)) return;
5971
+ if (inFlightToolCallSignaturesRef.current.has(toolCall.callSignature)) return;
5972
+ callsToRun.push(toolCall);
5973
+ });
5974
+ if (callsToRun.length === 0) {
5975
+ setPendingToolRequests(
5976
+ (prev) => prev.filter((request) => {
5977
+ const signature = getToolCallSignature(request.toolName, request.callId);
5978
+ if (!signature) return true;
5979
+ return !seenCallSignatures.has(signature);
5980
+ })
5981
+ );
5982
+ setActiveToolCalls([]);
5983
+ setIsLoading(false);
5984
+ return;
5985
+ }
5986
+ const callsToRunSignatures = new Set(callsToRun.map((toolCall) => toolCall.callSignature));
5987
+ setPendingToolRequests(
5988
+ (prev) => prev.filter((request) => {
5989
+ const signature = getToolCallSignature(request.toolName, request.callId);
5990
+ return !signature || !callsToRunSignatures.has(signature);
5991
+ })
5992
+ );
5993
+ callsToRun.forEach((toolCall) => {
5994
+ inFlightToolCallSignaturesRef.current.add(toolCall.callSignature);
5995
+ });
5996
+ setActiveToolCalls(
5997
+ callsToRun.map((toolCall) => ({
5998
+ toolName: toolCall.toolName,
5999
+ callId: toolCall.callId
6000
+ }))
6001
+ );
6002
+ const finalToolCalls = callsToRun.map((toolCall) => ({
6003
+ id: toolCall.callId,
6004
+ type: "tool_use",
6005
+ name: toolCall.toolName,
6006
+ input: toolCall.args,
6007
+ service: toolCall.serviceTag
6008
+ }));
6009
+ let finalToolResponses = [];
6010
+ try {
6011
+ const toolResponses = yield Promise.all(
6012
+ callsToRun.map((toolCall) => __async(void 0, null, function* () {
6013
+ var _a3, _b2, _c2, _d, _e, _f, _g;
6014
+ const mcpTool = toolList.find((tool) => tool.name === toolCall.toolName) || null;
6015
+ const localExecutor = localToolExecutors && typeof localToolExecutors[toolCall.toolName] === "function" ? localToolExecutors[toolCall.toolName] : null;
6016
+ if (localExecutor) {
6017
+ try {
6018
+ const localResult = yield localExecutor(toolCall.args, {
6019
+ toolName: toolCall.toolName,
6020
+ callId: toolCall.callId,
6021
+ serviceTag: toolCall.serviceTag,
6022
+ mcpTool
6023
+ });
6024
+ if (localResult && typeof localResult === "object" && !Array.isArray(localResult)) {
6025
+ const objectResult = localResult;
6026
+ const isError = objectResult.isError === true || objectResult.error === true;
6027
+ const maybeResult = Object.prototype.hasOwnProperty.call(objectResult, "result") ? objectResult.result : localResult;
6028
+ const textResult = typeof maybeResult === "string" ? maybeResult : JSON.stringify(maybeResult != null ? maybeResult : {});
6029
+ return {
6030
+ tool_call_id: toolCall.callId,
6031
+ tool_name: toolCall.toolName,
6032
+ result: textResult || "",
6033
+ isError
6034
+ };
6035
+ }
6036
+ return {
6037
+ tool_call_id: toolCall.callId,
6038
+ tool_name: toolCall.toolName,
6039
+ result: typeof localResult === "string" ? localResult : JSON.stringify(localResult != null ? localResult : {}),
6040
+ isError: false
6041
+ };
6042
+ } catch (error2) {
6043
+ return {
6044
+ tool_call_id: toolCall.callId,
6045
+ tool_name: toolCall.toolName,
6046
+ result: error2 instanceof Error ? error2.message : `Unhandled error calling ${toolCall.toolName}`,
6047
+ isError: true
6048
+ };
6049
+ }
6050
+ }
6051
+ if (!mcpTool) {
6052
+ console.error(`[AIChatPanel] Tool ${toolCall.toolName} not found in tool list`);
6053
+ return {
6054
+ tool_call_id: toolCall.callId,
6055
+ tool_name: toolCall.toolName,
6056
+ result: `Tool ${toolCall.toolName} not found in current tool list.`,
6057
+ isError: true
6058
+ };
6059
+ }
6060
+ const toolUrl = typeof mcpTool.url === "string" ? mcpTool.url : "";
6061
+ if (!toolUrl) {
6062
+ return {
6063
+ tool_call_id: toolCall.callId,
6064
+ tool_name: toolCall.toolName,
6065
+ result: `Tool ${toolCall.toolName} is missing url metadata.`,
6066
+ isError: true
6067
+ };
6068
+ }
6069
+ const body = {
6070
+ tool: toolCall.toolName,
6071
+ args: toolCall.args
6072
+ };
6073
+ try {
6074
+ const result = yield fetch(
6075
+ `${publicAPIUrl}/tools/${encodeURIComponent(toolUrl)}`,
6076
+ {
6077
+ method: "POST",
6078
+ headers: __spreadValues({
6079
+ "Content-Type": "application/json"
6080
+ }, yield buildMcpRequestHeaders({
6081
+ phase: "call",
6082
+ mcpServer: mcpTool,
6083
+ toolName: toolCall.toolName,
6084
+ toolArgs: toolCall.args
6085
+ })),
6086
+ body: JSON.stringify(body)
6087
+ }
6088
+ );
6089
+ if (!result.ok) {
6090
+ const errorBody = yield result.text();
6091
+ console.error(
6092
+ `[AIChatPanel] Tool call failed ${toolCall.toolName}: ${result.status} ${result.statusText} ${errorBody}`
6093
+ );
6094
+ return {
6095
+ tool_call_id: toolCall.callId,
6096
+ tool_name: toolCall.toolName,
6097
+ result: `HTTP ${result.status} ${result.statusText}: ${errorBody || "Tool call failed"}`,
6098
+ isError: true
6099
+ };
6100
+ }
6101
+ let resultData = null;
6102
+ try {
6103
+ resultData = yield result.json();
6104
+ } catch (error2) {
6105
+ console.error("[AIChatPanel] Failed parsing tool call JSON response:", error2);
6106
+ return {
6107
+ tool_call_id: toolCall.callId,
6108
+ tool_name: toolCall.toolName,
6109
+ result: "Tool returned a non-JSON response.",
6110
+ isError: true
6111
+ };
6112
+ }
6113
+ 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);
6114
+ 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";
6115
+ 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 : "";
6116
+ return {
6117
+ tool_call_id: toolCall.callId,
6118
+ tool_name: toolCall.toolName,
6119
+ result: normalizedResultText,
6120
+ isError: inferredError
6121
+ };
6122
+ } catch (error2) {
6123
+ console.error(`[AIChatPanel] Error calling tool ${toolCall.toolName}:`, error2);
6124
+ return {
6125
+ tool_call_id: toolCall.callId,
6126
+ tool_name: toolCall.toolName,
6127
+ result: error2 instanceof Error ? error2.message : `Unhandled error calling ${toolCall.toolName}`,
6128
+ isError: true
6129
+ };
6130
+ }
6131
+ }))
6132
+ );
6133
+ finalToolResponses = toolResponses.filter(Boolean);
6134
+ } finally {
6135
+ callsToRun.forEach((toolCall) => {
6136
+ inFlightToolCallSignaturesRef.current.delete(toolCall.callSignature);
6137
+ handledToolCallSignaturesRef.current.add(toolCall.callSignature);
6138
+ });
6139
+ }
6140
+ setActiveToolCalls([]);
6141
+ const currentLastKey = lastKeyRef.current;
6142
+ if (currentLastKey) {
6143
+ setHistory((prev) => {
6144
+ const existingEntry = prev[currentLastKey] || { content: "", callId: "" };
6145
+ return __spreadProps(__spreadValues({}, prev), {
6146
+ [currentLastKey]: __spreadProps(__spreadValues({}, existingEntry), {
6147
+ toolCalls: [...existingEntry.toolCalls || [], ...finalToolCalls],
6148
+ toolResponses: [...existingEntry.toolResponses || [], ...finalToolResponses]
6149
+ })
6150
+ });
6151
+ });
6152
+ }
6153
+ const toReplayText = (value) => {
6154
+ const raw = typeof value === "string" ? value : (() => {
6155
+ try {
6156
+ return JSON.stringify(value);
6157
+ } catch (_error) {
6158
+ return String(value != null ? value : "");
6159
+ }
6160
+ })();
6161
+ return String(raw != null ? raw : "");
6162
+ };
6163
+ const replayEntryKey = lastKeyRef.current || "";
6164
+ const previousReplayEntries = replayEntryKey ? toolReplaySummariesByKeyRef.current[replayEntryKey] || [] : [];
6165
+ const currentReplayEntries = callsToRun.map((toolCall, index) => {
6166
+ var _a3;
6167
+ const matchedResponse = finalToolResponses.find((response2) => (response2 == null ? void 0 : response2.tool_call_id) === toolCall.callId) || finalToolResponses[index];
6168
+ return {
6169
+ toolName: toolCall.toolName,
6170
+ callId: toolCall.callId,
6171
+ status: (matchedResponse == null ? void 0 : matchedResponse.isError) ? "error" : "ok",
6172
+ argsText: toReplayText(toolCall.args),
6173
+ resultText: toReplayText((_a3 = matchedResponse == null ? void 0 : matchedResponse.result) != null ? _a3 : "No result returned")
6174
+ };
6175
+ });
6176
+ const replayEntries = [...previousReplayEntries, ...currentReplayEntries];
6177
+ if (replayEntryKey) {
6178
+ toolReplaySummariesByKeyRef.current[replayEntryKey] = replayEntries;
6179
+ }
6180
+ const replayLines = replayEntries.map(
6181
+ (entry) => [
6182
+ `Tool: ${entry.toolName}`,
6183
+ `Call ID: ${entry.callId}`,
6184
+ `Status: ${entry.status}`,
6185
+ `Args: ${entry.argsText}`,
6186
+ `Result: ${entry.resultText}`
6187
+ ].join("\n")
6188
+ );
6189
+ const originalRequest = typeof lastPromptRef.current === "string" && lastPromptRef.current.trim() || typeof userPrompt === "string" && userPrompt.trim() || "";
6190
+ const continuationPromptText = [
6191
+ originalRequest ? `Original request: ${originalRequest}` : "",
6192
+ "Tool execution summary for the previous request:",
6193
+ ...replayLines,
6194
+ "Continue the same assistant response from exactly where you paused using these tool results.",
6195
+ "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.",
6196
+ "If you include meta tags, use only <thinking>, <reasoning>, <searching> for internal process.",
6197
+ "Put the final user-facing answer outside all meta tags."
6198
+ ].filter(Boolean).join("\n\n");
6199
+ if (continuationPromptText.length > MAX_TOOL_REPLAY_PAYLOAD_CHARS) {
6200
+ setActiveToolCalls([]);
6201
+ setIsLoading(false);
6202
+ setError({
6203
+ message: "Tool result payload is too large to continue safely in a single turn. Narrow the query or fetch steps in chunks.",
6204
+ code: "TOOL_REPLAY_TOO_LARGE"
6205
+ });
6206
+ return;
6207
+ }
6208
+ newMessages.push({
6209
+ role: "user",
6210
+ content: [
6211
+ {
6212
+ type: "text",
6213
+ text: continuationPromptText
6214
+ }
6215
+ ]
6216
+ });
6217
+ if (toolContinuationCountRef.current >= MAX_TOOL_CONTINUATIONS_PER_TURN) {
6218
+ setActiveToolCalls([]);
6219
+ setIsLoading(false);
6220
+ setError({
6221
+ message: "Tool continuation limit reached for this response. Please refine the prompt and retry.",
6222
+ code: "TOOL_CONTINUATION_LIMIT"
6223
+ });
6224
+ return;
6225
+ }
6226
+ toolContinuationCountRef.current += 1;
6227
+ if (!streamIdleRef.current) {
6228
+ yield waitForStreamIdle(1e4);
6229
+ }
6230
+ if (!streamIdleRef.current) {
6231
+ suppressAbortHistoryUpdateRef.current = true;
6232
+ try {
6233
+ stop(lastController);
6234
+ yield waitForStreamIdle(3e3);
6235
+ } finally {
6236
+ suppressAbortHistoryUpdateRef.current = false;
6237
+ }
6238
+ }
6239
+ if (!streamIdleRef.current) {
6240
+ setActiveToolCalls([]);
6241
+ setIsLoading(false);
6242
+ setError({
6243
+ message: "Timed out waiting for the previous stream to settle before tool continuation.",
6244
+ code: "TOOL_CONTINUATION_WAIT_TIMEOUT"
6245
+ });
6246
+ return;
6247
+ }
6248
+ const newController = new AbortController();
6249
+ setLastController(newController);
6250
+ const continuationKey = lastKeyRef.current;
6251
+ if (continuationKey) {
6252
+ const continuationBase = ((_c = latestHistoryRef.current[continuationKey]) == null ? void 0 : _c.content) || "";
6253
+ activeStreamAppendBaseRef.current = continuationBase.trim().length > 0 ? { key: continuationKey, base: continuationBase } : null;
6254
+ } else {
6255
+ activeStreamAppendBaseRef.current = null;
6256
+ }
6257
+ send(
6258
+ "",
6259
+ newMessages,
6260
+ [
6261
+ ...dataWithExtras(),
6262
+ {
6263
+ key: "--messages",
6264
+ data: newMessages.length.toString()
6265
+ }
6266
+ ],
6267
+ true,
6268
+ true,
6269
+ service,
6270
+ currentConversation,
6271
+ newController,
6272
+ void 0,
6273
+ (errorMsg) => {
6274
+ setActiveToolCalls([]);
6275
+ setIsLoading(false);
6276
+ setError({
6277
+ message: errorMsg,
6278
+ code: "TOOL_ERROR"
6279
+ });
6280
+ }
6281
+ );
6282
+ } finally {
6283
+ toolRequestProcessingRef.current = false;
6284
+ const queued = queuedToolRequestsRef.current;
6285
+ if (queued && queued.length > 0) {
6286
+ queuedToolRequestsRef.current = null;
6287
+ queueMicrotask(() => {
6288
+ void processGivenToolRequests(queued);
6289
+ });
6290
+ }
6291
+ }
6292
+ }),
6293
+ [
6294
+ toolList,
6295
+ localToolExecutors,
6296
+ publicAPIUrl,
6297
+ buildMcpRequestHeaders,
6298
+ dataWithExtras,
6299
+ send,
6300
+ service,
6301
+ currentConversation,
6302
+ setActiveToolCalls,
6303
+ getToolCallSignature,
6304
+ traceContextMode,
6305
+ idle,
6306
+ stop,
6307
+ lastController,
6308
+ waitForStreamIdle
6309
+ ]
6310
+ );
6311
+ const handleToolApproval = useCallback2(
6312
+ (toolName, scope) => {
6313
+ const normalizedToolName = normalizeToolName(toolName);
6314
+ if (!normalizedToolName) return;
6315
+ if (scope === "session" || scope === "always") {
6316
+ setSessionApprovedTools((prev) => Array.from(/* @__PURE__ */ new Set([...prev, normalizedToolName])));
6317
+ }
6318
+ if (scope === "always") {
6319
+ setAlwaysApprovedTools((prev) => Array.from(/* @__PURE__ */ new Set([...prev, normalizedToolName])));
6320
+ }
6321
+ const requestsToRun = (pendingToolRequestsRef.current || []).filter(
6322
+ (r) => normalizeToolName(r.toolName) === normalizedToolName
6323
+ );
6324
+ void processGivenToolRequests(requestsToRun);
6325
+ setPendingToolRequests(
6326
+ (prev) => prev.filter((r) => normalizeToolName(r.toolName) !== normalizedToolName)
6327
+ );
6328
+ },
6329
+ [processGivenToolRequests, normalizeToolName]
6330
+ );
6331
+ useEffect8(() => {
6332
+ if (pendingToolRequests.length === 0) return;
6333
+ const configuredAutoApproveTools = Array.isArray(autoApproveTools) ? new Set(
6334
+ autoApproveTools.map((toolName) => normalizeToolName(String(toolName))).filter(Boolean)
6335
+ ) : null;
6336
+ const toAuto = pendingToolRequests.filter(
6337
+ (r) => {
6338
+ const normalized = normalizeToolName(r.toolName);
6339
+ if (!normalized) return false;
6340
+ if (autoApproveTools === true) return true;
6341
+ if (configuredAutoApproveTools == null ? void 0 : configuredAutoApproveTools.has(normalized)) return true;
6342
+ return sessionApprovedTools.includes(normalized) || alwaysApprovedTools.includes(normalized);
6343
+ }
6344
+ );
6345
+ if (toAuto.length > 0) {
6346
+ void processGivenToolRequests(toAuto);
6347
+ setPendingToolRequests(
6348
+ (prev) => prev.filter(
6349
+ (r) => {
6350
+ const normalized = normalizeToolName(r.toolName);
6351
+ if (!normalized) return true;
6352
+ if (autoApproveTools === true) return false;
6353
+ if (configuredAutoApproveTools == null ? void 0 : configuredAutoApproveTools.has(normalized)) return false;
6354
+ return !sessionApprovedTools.includes(normalized) && !alwaysApprovedTools.includes(normalized);
6355
+ }
6356
+ )
6357
+ );
6358
+ }
6359
+ }, [
6360
+ autoApproveTools,
6361
+ pendingToolRequests,
6362
+ sessionApprovedTools,
6363
+ alwaysApprovedTools,
6364
+ processGivenToolRequests,
6365
+ normalizeToolName
6366
+ ]);
4548
6367
  const cleanContentForDisplay = useCallback2((content) => {
4549
6368
  let cleaned = content.replace(/\*\*(.*?)\*\*/g, "$1").replace(/\*(.*?)\*/g, "$1").replace(/\n+/g, " ").replace(/\s+/g, " ").trim();
4550
6369
  if (cleaned.length > 100) {
@@ -4553,7 +6372,6 @@ var AIChatPanel = ({
4553
6372
  return cleaned || "Thinking";
4554
6373
  }, []);
4555
6374
  const processThinkingTags = useCallback2((text) => {
4556
- var _a2, _b, _c;
4557
6375
  if (!text) {
4558
6376
  return {
4559
6377
  cleanedText: "",
@@ -4563,30 +6381,28 @@ var AIChatPanel = ({
4563
6381
  };
4564
6382
  }
4565
6383
  const processedText = text.replace(/\u200B/g, "");
4566
- const allMatches = [];
4567
- const thinkingRegex = /<thinking>([\s\S]*?)<\/thinking>/gi;
4568
- let match;
4569
- while ((match = thinkingRegex.exec(processedText)) !== null) {
4570
- const content = (_a2 = match[1]) == null ? void 0 : _a2.trim();
4571
- if (content) {
4572
- allMatches.push({ content, index: match.index, type: "thinking" });
4573
- }
4574
- }
4575
- const reasoningRegex = /<reasoning>([\s\S]*?)<\/reasoning>/gi;
4576
- while ((match = reasoningRegex.exec(processedText)) !== null) {
4577
- const content = (_b = match[1]) == null ? void 0 : _b.trim();
4578
- if (content) {
4579
- allMatches.push({ content, index: match.index, type: "reasoning" });
4580
- }
4581
- }
4582
- const searchingRegex = /<searching>([\s\S]*?)<\/searching>/gi;
4583
- while ((match = searchingRegex.exec(processedText)) !== null) {
4584
- const content = (_c = match[1]) == null ? void 0 : _c.trim();
4585
- if (content) {
4586
- allMatches.push({ content, index: match.index, type: "searching" });
6384
+ const completedBlocks = [];
6385
+ const textWithCompleteMarkers = processedText.replace(
6386
+ /<(thinking|reasoning|searching)>([\s\S]*?)<\/\1>/gi,
6387
+ (_fullMatch, rawType, rawContent, offset) => {
6388
+ const normalizedType = String(rawType || "").trim().toLowerCase();
6389
+ const type = normalizedType === "reasoning" ? "reasoning" : normalizedType === "searching" ? "searching" : "thinking";
6390
+ const content = String(rawContent || "").trim();
6391
+ if (!content) return "\n\n";
6392
+ const signature = getThinkingBlockSignature(type, content);
6393
+ completedBlocks.push({
6394
+ type,
6395
+ content,
6396
+ index: Number(offset || 0),
6397
+ signature
6398
+ });
6399
+ return `
6400
+
6401
+ ${buildThinkingBlockMarker(type, signature)}
6402
+
6403
+ `;
4587
6404
  }
4588
- }
4589
- const completedBlocks = allMatches.sort((a, b) => a.index - b.index);
6405
+ );
4590
6406
  let activeBlock = null;
4591
6407
  const tagTypes = ["thinking", "reasoning", "searching"];
4592
6408
  let latestIncompletePos = -1;
@@ -4616,7 +6432,7 @@ var AIChatPanel = ({
4616
6432
  }
4617
6433
  }
4618
6434
  }
4619
- 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();
6435
+ 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();
4620
6436
  let lastThinkingContent = "Thinking";
4621
6437
  if (completedBlocks.length > 0) {
4622
6438
  const lastBlock = completedBlocks[completedBlocks.length - 1];
@@ -4628,15 +6444,67 @@ var AIChatPanel = ({
4628
6444
  }
4629
6445
  return { cleanedText, completedBlocks, activeBlock, lastThinkingContent };
4630
6446
  }, [cleanContentForDisplay]);
6447
+ const mergeThinkingBlocks = useCallback2(
6448
+ (existing, incoming) => {
6449
+ if (incoming.length === 0) return existing;
6450
+ if (existing.length === 0) return incoming;
6451
+ const incomingSupersetPrefix = incoming.length >= existing.length && existing.every((block, index) => {
6452
+ const next = incoming[index];
6453
+ return !!next && next.type === block.type && next.content === block.content;
6454
+ });
6455
+ if (incomingSupersetPrefix) {
6456
+ return incoming;
6457
+ }
6458
+ const merged = [...existing];
6459
+ const seen = new Set(existing.map((block) => block.signature || `${block.type}::${block.content}`));
6460
+ for (const block of incoming) {
6461
+ const signature = block.signature || `${block.type}::${block.content}`;
6462
+ if (seen.has(signature)) continue;
6463
+ seen.add(signature);
6464
+ merged.push(block);
6465
+ }
6466
+ return merged;
6467
+ },
6468
+ []
6469
+ );
6470
+ const getThinkingBlockCollapseKey = useCallback2((entryKey, blockKey) => {
6471
+ return `${entryKey}::${blockKey}`;
6472
+ }, []);
6473
+ const getThinkingBlockRenderKey = useCallback2((block, fallbackIndex) => {
6474
+ return String((block == null ? void 0 : block.signature) || "").trim() || `block-${fallbackIndex}`;
6475
+ }, []);
6476
+ const AGENT_SUGGESTION_ACTION = {
6477
+ pattern: "\\[SUGGEST_AGENT:([^|\\]]+)\\|([^|\\]]+)\\|([^\\]]+)\\]",
6478
+ markdown: '<agent-suggestion data-agent-id="$1" data-agent-name="$2" data-reason="$3"></agent-suggestion>'
6479
+ };
6480
+ const extractToolRequests = useCallback2((rawResponse) => {
6481
+ return extractToolRequestMatchesFromText(rawResponse);
6482
+ }, []);
6483
+ const formatToolRequestsForDisplay = useCallback2((rawResponse) => {
6484
+ if (!rawResponse) return rawResponse;
6485
+ const requests = extractToolRequestMatchesFromText(rawResponse);
6486
+ if (requests.length === 0) return stripStandaloneRawToolJsonLines(rawResponse);
6487
+ const output = [];
6488
+ let cursor = 0;
6489
+ requests.forEach((request) => {
6490
+ const start = Math.max(0, Math.min(rawResponse.length, request.start));
6491
+ const end = Math.max(start, Math.min(rawResponse.length, request.end));
6492
+ output.push(rawResponse.slice(cursor, start));
6493
+ output.push(`
6494
+
6495
+ ${buildInlineToolMarker(request.toolName, request.callId)}
6496
+
6497
+ `);
6498
+ cursor = end;
6499
+ });
6500
+ output.push(rawResponse.slice(cursor));
6501
+ return stripStandaloneRawToolJsonLines(output.join(""));
6502
+ }, []);
4631
6503
  const activeThinkingBlock = useMemo2(() => {
4632
6504
  if (!response || justReset) return null;
4633
6505
  const { activeBlock } = processThinkingTags(response);
4634
6506
  return activeBlock;
4635
6507
  }, [response, justReset, processThinkingTags]);
4636
- const AGENT_SUGGESTION_ACTION = {
4637
- pattern: "\\[SUGGEST_AGENT:([^|\\]]+)\\|([^|\\]]+)\\|([^\\]]+)\\]",
4638
- markdown: '<agent-suggestion data-agent-id="$1" data-agent-name="$2" data-reason="$3"></agent-suggestion>'
4639
- };
4640
6508
  const processActions = useCallback2((content) => {
4641
6509
  const allActions = [AGENT_SUGGESTION_ACTION, ...actions || []];
4642
6510
  let processed = content;
@@ -4670,6 +6538,38 @@ var AIChatPanel = ({
4670
6538
  }
4671
6539
  return displayPrompt;
4672
6540
  }, [hideRagContextInPrompt]);
6541
+ const normalizeHistoryPromptForContext = useCallback2((historyPrompt) => {
6542
+ let promptForHistory = String(historyPrompt || "");
6543
+ const isoTimestampRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z:/;
6544
+ if (isoTimestampRegex.test(promptForHistory)) {
6545
+ const colonIndex = promptForHistory.indexOf(":", 19);
6546
+ promptForHistory = promptForHistory.substring(colonIndex + 1);
6547
+ } else if (/^\d+:/.test(promptForHistory)) {
6548
+ const colonIndex = promptForHistory.indexOf(":");
6549
+ promptForHistory = promptForHistory.substring(colonIndex + 1);
6550
+ }
6551
+ return promptForHistory;
6552
+ }, []);
6553
+ const buildAssistantContextContent = useCallback2(
6554
+ (historyPrompt, historyEntry) => {
6555
+ const assistantBaseContent = typeof (historyEntry == null ? void 0 : historyEntry.content) === "string" ? historyEntry.content : "";
6556
+ if (traceContextMode !== "full") {
6557
+ return assistantBaseContent;
6558
+ }
6559
+ const traceSummary = buildCompactTraceSummary({
6560
+ reasoningBlocks: thinkingBlocksByKeyRef.current[historyPrompt] || [],
6561
+ toolCalls: historyEntry == null ? void 0 : historyEntry.toolCalls,
6562
+ toolResponses: historyEntry == null ? void 0 : historyEntry.toolResponses
6563
+ });
6564
+ if (!traceSummary) {
6565
+ return assistantBaseContent;
6566
+ }
6567
+ return assistantBaseContent ? `${assistantBaseContent}
6568
+
6569
+ ${traceSummary}` : traceSummary;
6570
+ },
6571
+ [traceContextMode]
6572
+ );
4673
6573
  const interactionClicked = useCallback2((callId, action, emailaddress = "", comment = "") => __async(void 0, null, function* () {
4674
6574
  var _a2, _b;
4675
6575
  console.log(`[AIChatPanel] Interaction: ${action} for callId: ${callId}`);
@@ -4752,9 +6652,24 @@ var AIChatPanel = ({
4752
6652
  lastScrollTimeRef.current = now;
4753
6653
  }, []);
4754
6654
  const continueChat = useCallback2((promptText) => {
6655
+ handledToolCallSignaturesRef.current = /* @__PURE__ */ new Set();
6656
+ inFlightToolCallSignaturesRef.current = /* @__PURE__ */ new Set();
6657
+ toolContinuationCountRef.current = 0;
6658
+ activeStreamAppendBaseRef.current = null;
6659
+ toolReplaySummariesByKeyRef.current = {};
6660
+ setPendingToolRequests([]);
6661
+ setActiveToolCalls([]);
6662
+ setCollapsedBlocks((prev) => {
6663
+ const next = new Set(prev);
6664
+ Object.entries(thinkingBlocksByKeyRef.current).forEach(([entryKey, blocks]) => {
6665
+ blocks.forEach((block, index) => {
6666
+ next.add(getThinkingBlockCollapseKey(entryKey, getThinkingBlockRenderKey(block, index)));
6667
+ });
6668
+ });
6669
+ return next;
6670
+ });
4755
6671
  setThinkingBlocks([]);
4756
6672
  setCurrentThinkingIndex(0);
4757
- setCollapsedBlocks(/* @__PURE__ */ new Set());
4758
6673
  hasAutoCollapsedRef.current = false;
4759
6674
  prevBlockCountRef.current = 0;
4760
6675
  setError(null);
@@ -4783,6 +6698,7 @@ var AIChatPanel = ({
4783
6698
  setHistory((prevHistory) => __spreadProps(__spreadValues({}, prevHistory), {
4784
6699
  [promptKey]: { content: "", callId: "" }
4785
6700
  }));
6701
+ toolReplaySummariesByKeyRef.current[promptKey] = [];
4786
6702
  setLastPrompt(promptToSend.trim());
4787
6703
  setLastKey(promptKey);
4788
6704
  setTimeout(() => {
@@ -4791,20 +6707,14 @@ var AIChatPanel = ({
4791
6707
  console.log("AIChatPanel.continueChat - about to call ensureConversation");
4792
6708
  ensureConversation().then((convId) => {
4793
6709
  console.log("AIChatPanel.continueChat - ensureConversation resolved with:", convId);
6710
+ const historyForCall = latestHistoryRef.current || {};
4794
6711
  const messagesAndHistory = [];
4795
- Object.entries(history).forEach(([historyPrompt, historyEntry]) => {
6712
+ Object.entries(historyForCall).forEach(([historyPrompt, historyEntry]) => {
4796
6713
  if (historyPrompt === promptKey) return;
4797
- let promptForHistory = historyPrompt;
4798
- const isoTimestampRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z:/;
4799
- if (isoTimestampRegex.test(historyPrompt)) {
4800
- const colonIndex = historyPrompt.indexOf(":", 19);
4801
- promptForHistory = historyPrompt.substring(colonIndex + 1);
4802
- } else if (/^\d+:/.test(historyPrompt)) {
4803
- const colonIndex = historyPrompt.indexOf(":");
4804
- promptForHistory = historyPrompt.substring(colonIndex + 1);
4805
- }
6714
+ const promptForHistory = normalizeHistoryPromptForContext(historyPrompt);
6715
+ const assistantContextContent = buildAssistantContextContent(historyPrompt, historyEntry);
4806
6716
  messagesAndHistory.push({ role: "user", content: promptForHistory });
4807
- messagesAndHistory.push({ role: "assistant", content: historyEntry.content });
6717
+ messagesAndHistory.push({ role: "assistant", content: assistantContextContent });
4808
6718
  });
4809
6719
  let fullPromptToSend = promptToSend.trim();
4810
6720
  if (messagesAndHistory.length === 0 && promptTemplate) {
@@ -4812,6 +6722,19 @@ var AIChatPanel = ({
4812
6722
  }
4813
6723
  const newController = new AbortController();
4814
6724
  setLastController(newController);
6725
+ if (onBeforeSend) {
6726
+ void Promise.resolve(
6727
+ onBeforeSend({
6728
+ prompt: promptToSend.trim(),
6729
+ conversationId: convId || null,
6730
+ agentId: agent,
6731
+ service,
6732
+ messages: messagesAndHistory
6733
+ })
6734
+ ).catch((error2) => {
6735
+ console.warn("[AIChatPanel] onBeforeSend callback failed:", error2);
6736
+ });
6737
+ }
4815
6738
  send(
4816
6739
  fullPromptToSend,
4817
6740
  messagesAndHistory,
@@ -4834,14 +6757,21 @@ var AIChatPanel = ({
4834
6757
  console.log("[AIChatPanel] Error callback triggered:", errorMsg);
4835
6758
  const isAbortError = errorMsg.toLowerCase().includes("abort") || errorMsg.toLowerCase().includes("canceled") || errorMsg.toLowerCase().includes("cancelled");
4836
6759
  if (isAbortError) {
6760
+ if (suppressAbortHistoryUpdateRef.current) {
6761
+ setIsLoading(false);
6762
+ return;
6763
+ }
4837
6764
  console.log("[AIChatPanel] Request was aborted by user");
4838
6765
  if (promptKey) {
4839
- setHistory((prev) => __spreadProps(__spreadValues({}, prev), {
4840
- [promptKey]: {
4841
- content: "Response canceled",
4842
- callId: lastCallId || ""
4843
- }
4844
- }));
6766
+ setHistory((prev) => {
6767
+ const existingEntry = prev[promptKey] || { content: "", callId: "" };
6768
+ return __spreadProps(__spreadValues({}, prev), {
6769
+ [promptKey]: __spreadProps(__spreadValues({}, existingEntry), {
6770
+ content: "Response canceled",
6771
+ callId: lastCallId || existingEntry.callId || ""
6772
+ })
6773
+ });
6774
+ });
4845
6775
  }
4846
6776
  } else if (errorMsg.includes("413") || errorMsg.toLowerCase().includes("content too large")) {
4847
6777
  setError({
@@ -4849,12 +6779,15 @@ var AIChatPanel = ({
4849
6779
  code: "413"
4850
6780
  });
4851
6781
  if (promptKey) {
4852
- setHistory((prev) => __spreadProps(__spreadValues({}, prev), {
4853
- [promptKey]: {
4854
- content: `Error: ${errorMsg}`,
4855
- callId: lastCallId || ""
4856
- }
4857
- }));
6782
+ setHistory((prev) => {
6783
+ const existingEntry = prev[promptKey] || { content: "", callId: "" };
6784
+ return __spreadProps(__spreadValues({}, prev), {
6785
+ [promptKey]: __spreadProps(__spreadValues({}, existingEntry), {
6786
+ content: `Error: ${errorMsg}`,
6787
+ callId: lastCallId || existingEntry.callId || ""
6788
+ })
6789
+ });
6790
+ });
4858
6791
  }
4859
6792
  } else if (errorMsg.toLowerCase().includes("network error") || errorMsg.toLowerCase().includes("fetch")) {
4860
6793
  setError({
@@ -4862,12 +6795,15 @@ var AIChatPanel = ({
4862
6795
  code: "NETWORK_ERROR"
4863
6796
  });
4864
6797
  if (promptKey) {
4865
- setHistory((prev) => __spreadProps(__spreadValues({}, prev), {
4866
- [promptKey]: {
4867
- content: `Error: ${errorMsg}`,
4868
- callId: lastCallId || ""
4869
- }
4870
- }));
6798
+ setHistory((prev) => {
6799
+ const existingEntry = prev[promptKey] || { content: "", callId: "" };
6800
+ return __spreadProps(__spreadValues({}, prev), {
6801
+ [promptKey]: __spreadProps(__spreadValues({}, existingEntry), {
6802
+ content: `Error: ${errorMsg}`,
6803
+ callId: lastCallId || existingEntry.callId || ""
6804
+ })
6805
+ });
6806
+ });
4871
6807
  }
4872
6808
  } else {
4873
6809
  setError({
@@ -4875,12 +6811,15 @@ var AIChatPanel = ({
4875
6811
  code: "UNKNOWN_ERROR"
4876
6812
  });
4877
6813
  if (promptKey) {
4878
- setHistory((prev) => __spreadProps(__spreadValues({}, prev), {
4879
- [promptKey]: {
4880
- content: `Error: ${errorMsg}`,
4881
- callId: lastCallId || ""
4882
- }
4883
- }));
6814
+ setHistory((prev) => {
6815
+ const existingEntry = prev[promptKey] || { content: "", callId: "" };
6816
+ return __spreadProps(__spreadValues({}, prev), {
6817
+ [promptKey]: __spreadProps(__spreadValues({}, existingEntry), {
6818
+ content: `Error: ${errorMsg}`,
6819
+ callId: lastCallId || existingEntry.callId || ""
6820
+ })
6821
+ });
6822
+ });
4884
6823
  }
4885
6824
  }
4886
6825
  setIsLoading(false);
@@ -4902,14 +6841,20 @@ var AIChatPanel = ({
4902
6841
  lastCallId,
4903
6842
  processThinkingTags,
4904
6843
  clearFollowOnQuestionsNextPrompt,
4905
- history,
4906
6844
  promptTemplate,
4907
6845
  send,
4908
6846
  service,
6847
+ agent,
4909
6848
  ensureConversation,
6849
+ normalizeHistoryPromptForContext,
6850
+ buildAssistantContextContent,
6851
+ traceContextMode,
4910
6852
  dataWithExtras,
4911
6853
  scrollToBottom,
4912
6854
  onConversationCreated,
6855
+ onBeforeSend,
6856
+ getThinkingBlockCollapseKey,
6857
+ getThinkingBlockRenderKey,
4913
6858
  setResponse
4914
6859
  ]);
4915
6860
  const handleSuggestionClick = useCallback2((question) => {
@@ -4937,6 +6882,14 @@ var AIChatPanel = ({
4937
6882
  setLastKey(null);
4938
6883
  setIsLoading(false);
4939
6884
  setCurrentConversation(null);
6885
+ thinkingBlocksByKeyRef.current = {};
6886
+ setThinkingBlocksByKey({});
6887
+ handledToolCallSignaturesRef.current = /* @__PURE__ */ new Set();
6888
+ inFlightToolCallSignaturesRef.current = /* @__PURE__ */ new Set();
6889
+ toolContinuationCountRef.current = 0;
6890
+ activeStreamAppendBaseRef.current = null;
6891
+ toolReplaySummariesByKeyRef.current = {};
6892
+ setPendingToolRequests([]);
4940
6893
  setFollowOnQuestionsState(followOnQuestions);
4941
6894
  setThinkingBlocks([]);
4942
6895
  setCurrentThinkingIndex(0);
@@ -4947,6 +6900,7 @@ var AIChatPanel = ({
4947
6900
  setLastController(new AbortController());
4948
6901
  setUserHasScrolled(false);
4949
6902
  setError(null);
6903
+ setActiveToolCalls([]);
4950
6904
  setTimeout(() => {
4951
6905
  var _a2;
4952
6906
  setJustReset(false);
@@ -4955,43 +6909,85 @@ var AIChatPanel = ({
4955
6909
  }, [newConversationConfirm, idle, stop, lastController, setResponse, followOnQuestions]);
4956
6910
  useEffect8(() => {
4957
6911
  if (!response || !lastKey || justReset) return;
4958
- const { cleanedText, completedBlocks } = processThinkingTags(response);
4959
- setThinkingBlocks(completedBlocks);
4960
- if (completedBlocks.length > prevBlockCountRef.current) {
6912
+ const extractedToolRequests = extractToolRequests(response);
6913
+ const seenToolCallSignatures = /* @__PURE__ */ new Set();
6914
+ const unseenToolRequests = extractedToolRequests.filter((request) => {
6915
+ const callSignature = getToolCallSignature(request.toolName, request.callId);
6916
+ if (!callSignature) return false;
6917
+ if (seenToolCallSignatures.has(callSignature)) return false;
6918
+ seenToolCallSignatures.add(callSignature);
6919
+ if (handledToolCallSignaturesRef.current.has(callSignature)) return false;
6920
+ if (inFlightToolCallSignaturesRef.current.has(callSignature)) return false;
6921
+ return true;
6922
+ });
6923
+ setPendingToolRequests((prev) => {
6924
+ if (areToolRequestListsEqual(prev, unseenToolRequests)) {
6925
+ return prev;
6926
+ }
6927
+ return unseenToolRequests;
6928
+ });
6929
+ const responseWithInlineToolLabels = formatToolRequestsForDisplay(response);
6930
+ const { cleanedText: parsedCleanedText, completedBlocks } = processThinkingTags(responseWithInlineToolLabels);
6931
+ const cleanedText = parsedCleanedText.trim();
6932
+ const existingBlocks = thinkingBlocksByKeyRef.current[lastKey] || [];
6933
+ const mergedBlocks = mergeThinkingBlocks(existingBlocks, completedBlocks);
6934
+ thinkingBlocksByKeyRef.current[lastKey] = mergedBlocks;
6935
+ setThinkingBlocksByKey((prev) => {
6936
+ const existing = prev[lastKey];
6937
+ const isSame = !!existing && existing.length === mergedBlocks.length && existing.every(
6938
+ (block, index) => {
6939
+ var _a2, _b;
6940
+ return block.type === ((_a2 = mergedBlocks[index]) == null ? void 0 : _a2.type) && block.content === ((_b = mergedBlocks[index]) == null ? void 0 : _b.content);
6941
+ }
6942
+ );
6943
+ if (isSame) return prev;
6944
+ return __spreadProps(__spreadValues({}, prev), {
6945
+ [lastKey]: mergedBlocks
6946
+ });
6947
+ });
6948
+ setThinkingBlocks(mergedBlocks);
6949
+ setCurrentThinkingIndex(Math.max(0, mergedBlocks.length - 1));
6950
+ if (mergedBlocks.length > prevBlockCountRef.current) {
4961
6951
  setCollapsedBlocks((prev) => {
4962
6952
  const next = new Set(prev);
4963
- for (let i = 0; i < completedBlocks.length - 1; i++) {
4964
- next.add(`block-${i}`);
6953
+ for (let i = 0; i < mergedBlocks.length - 1; i++) {
6954
+ const block = mergedBlocks[i];
6955
+ if (!block) continue;
6956
+ next.add(getThinkingBlockCollapseKey(lastKey, getThinkingBlockRenderKey(block, i)));
4965
6957
  }
4966
6958
  return next;
4967
6959
  });
4968
- prevBlockCountRef.current = completedBlocks.length;
4969
- }
4970
- const hasMainContent = cleanedText.trim().length > 0;
4971
- const hasThinkingContent = completedBlocks.length > 0 || processThinkingTags(response).activeBlock !== null;
4972
- if (hasMainContent && hasThinkingContent && !hasAutoCollapsedRef.current) {
4973
- hasAutoCollapsedRef.current = true;
4974
- setTimeout(() => {
4975
- setCollapsedBlocks((prev) => {
4976
- const next = new Set(prev);
4977
- completedBlocks.forEach((_, index) => next.add(`block-${index}`));
4978
- next.add("active");
4979
- return next;
4980
- });
4981
- }, 500);
6960
+ prevBlockCountRef.current = mergedBlocks.length;
4982
6961
  }
4983
6962
  setHistory((prev) => {
4984
6963
  const newHistory = __spreadValues({}, prev);
4985
- newHistory[lastKey] = {
4986
- content: cleanedText,
4987
- // Store raw content, not processed
4988
- callId: lastCallId || ""
4989
- };
6964
+ const existingEntry = newHistory[lastKey] || { content: "", callId: "" };
6965
+ const appendBase = activeStreamAppendBaseRef.current;
6966
+ const mergedContinuationContent = appendBase && appendBase.key === lastKey ? mergeContinuationResponseText(appendBase.base, cleanedText) : cleanedText;
6967
+ const existingContent = typeof existingEntry.content === "string" ? existingEntry.content : "";
6968
+ const nextContent = appendBase && appendBase.key === lastKey && existingContent.length > mergedContinuationContent.length ? existingContent : shouldPreserveBoundaryDroppedStreamText(existingContent, mergedContinuationContent) ? existingContent : mergedContinuationContent;
6969
+ newHistory[lastKey] = __spreadProps(__spreadValues({}, existingEntry), {
6970
+ content: nextContent,
6971
+ // Store raw content without tool JSON or thinking tags
6972
+ callId: lastCallId || existingEntry.callId || ""
6973
+ });
4990
6974
  latestHistoryRef.current = newHistory;
4991
6975
  return newHistory;
4992
6976
  });
4993
- }, [response, lastKey, lastCallId, processThinkingTags, justReset]);
6977
+ }, [
6978
+ response,
6979
+ lastKey,
6980
+ lastCallId,
6981
+ processThinkingTags,
6982
+ justReset,
6983
+ extractToolRequests,
6984
+ getToolCallSignature,
6985
+ getThinkingBlockCollapseKey,
6986
+ getThinkingBlockRenderKey,
6987
+ formatToolRequestsForDisplay
6988
+ ]);
4994
6989
  useEffect8(() => {
6990
+ var _a2;
4995
6991
  const wasStreaming = !prevIdleRef.current;
4996
6992
  const isNowIdle = idle;
4997
6993
  prevIdleRef.current = idle;
@@ -5015,6 +7011,12 @@ var AIChatPanel = ({
5015
7011
  if (!isNowIdle && hasNotifiedCompletionRef.current) {
5016
7012
  hasNotifiedCompletionRef.current = false;
5017
7013
  prevResponseLengthRef.current = 0;
7014
+ const currentLastKey = lastKeyRef.current;
7015
+ const existingContent = currentLastKey ? ((_a2 = latestHistoryRef.current[currentLastKey]) == null ? void 0 : _a2.content) || "" : "";
7016
+ activeStreamAppendBaseRef.current = currentLastKey && existingContent.trim().length > 0 ? {
7017
+ key: currentLastKey,
7018
+ base: existingContent
7019
+ } : null;
5018
7020
  }
5019
7021
  }, [idle]);
5020
7022
  useEffect8(() => {
@@ -5035,21 +7037,19 @@ var AIChatPanel = ({
5035
7037
  scrollToBottom(true);
5036
7038
  }
5037
7039
  }, [response, idle]);
5038
- const idleRef = useRef6(idle);
5039
- idleRef.current = idle;
5040
7040
  useEffect8(() => {
5041
7041
  const scrollArea = responseAreaRef.current;
5042
7042
  if (!scrollArea) return;
5043
7043
  const scrollViewport = scrollArea.querySelector(".ai-scroll-area-viewport");
5044
7044
  const scrollElement = scrollViewport || scrollArea;
5045
7045
  const handleWheel = (e) => {
5046
- if (idleRef.current) return;
7046
+ if (streamIdleRef.current) return;
5047
7047
  if (e.deltaY < 0 && !userHasScrolledRef.current) {
5048
7048
  setUserHasScrolled(true);
5049
7049
  }
5050
7050
  };
5051
7051
  const handleScroll = () => {
5052
- if (idleRef.current || !userHasScrolledRef.current) return;
7052
+ if (streamIdleRef.current || !userHasScrolledRef.current) return;
5053
7053
  const scrollHeight = scrollElement.scrollHeight;
5054
7054
  const currentScrollTop = scrollElement.scrollTop;
5055
7055
  const clientHeight = scrollElement.clientHeight;
@@ -5066,7 +7066,12 @@ var AIChatPanel = ({
5066
7066
  };
5067
7067
  }, []);
5068
7068
  useEffect8(() => {
5069
- setFollowOnQuestionsState(followOnQuestions);
7069
+ setFollowOnQuestionsState((prev) => {
7070
+ if (prev.length === followOnQuestions.length && prev.every((question, index) => question === followOnQuestions[index])) {
7071
+ return prev;
7072
+ }
7073
+ return followOnQuestions;
7074
+ });
5070
7075
  }, [followOnQuestions]);
5071
7076
  useEffect8(() => {
5072
7077
  const currentlyLoading = isLoading || !idle;
@@ -5084,10 +7089,11 @@ var AIChatPanel = ({
5084
7089
  const currentLastKey = lastKeyRef.current;
5085
7090
  const currentLastCallId = lastCallIdRef.current;
5086
7091
  if (currentLastKey && currentResponse) {
5087
- currentHistory[currentLastKey] = {
7092
+ const existingEntry = currentHistory[currentLastKey] || { content: "", callId: "" };
7093
+ currentHistory[currentLastKey] = __spreadProps(__spreadValues({}, existingEntry), {
5088
7094
  content: currentResponse + "\n\n(response interrupted)",
5089
- callId: currentLastCallId || ""
5090
- };
7095
+ callId: currentLastCallId || existingEntry.callId || ""
7096
+ });
5091
7097
  }
5092
7098
  if (historyCallbackRef.current && Object.keys(currentHistory).length > 0 && !hasNotifiedCompletionRef.current) {
5093
7099
  historyCallbackRef.current(currentHistory);
@@ -5124,12 +7130,15 @@ var AIChatPanel = ({
5124
7130
  code: "413"
5125
7131
  });
5126
7132
  if (lastKey) {
5127
- setHistory((prev) => __spreadProps(__spreadValues({}, prev), {
5128
- [lastKey]: {
5129
- content: `Error: ${errorMessage}`,
5130
- callId: lastCallId || ""
5131
- }
5132
- }));
7133
+ setHistory((prev) => {
7134
+ const existingEntry = prev[lastKey] || { content: "", callId: "" };
7135
+ return __spreadProps(__spreadValues({}, prev), {
7136
+ [lastKey]: __spreadProps(__spreadValues({}, existingEntry), {
7137
+ content: `Error: ${errorMessage}`,
7138
+ callId: lastCallId || existingEntry.callId || ""
7139
+ })
7140
+ });
7141
+ });
5133
7142
  }
5134
7143
  } else if (errorMessage.toLowerCase().includes("network error") || errorMessage.toLowerCase().includes("fetch")) {
5135
7144
  setError({
@@ -5137,12 +7146,15 @@ var AIChatPanel = ({
5137
7146
  code: "NETWORK_ERROR"
5138
7147
  });
5139
7148
  if (lastKey) {
5140
- setHistory((prev) => __spreadProps(__spreadValues({}, prev), {
5141
- [lastKey]: {
5142
- content: `Error: ${errorMessage}`,
5143
- callId: lastCallId || ""
5144
- }
5145
- }));
7149
+ setHistory((prev) => {
7150
+ const existingEntry = prev[lastKey] || { content: "", callId: "" };
7151
+ return __spreadProps(__spreadValues({}, prev), {
7152
+ [lastKey]: __spreadProps(__spreadValues({}, existingEntry), {
7153
+ content: `Error: ${errorMessage}`,
7154
+ callId: lastCallId || existingEntry.callId || ""
7155
+ })
7156
+ });
7157
+ });
5146
7158
  }
5147
7159
  } else {
5148
7160
  setError({
@@ -5150,12 +7162,15 @@ var AIChatPanel = ({
5150
7162
  code: "UNKNOWN_ERROR"
5151
7163
  });
5152
7164
  if (lastKey) {
5153
- setHistory((prev) => __spreadProps(__spreadValues({}, prev), {
5154
- [lastKey]: {
5155
- content: `Error: ${errorMessage}`,
5156
- callId: lastCallId || ""
5157
- }
5158
- }));
7165
+ setHistory((prev) => {
7166
+ const existingEntry = prev[lastKey] || { content: "", callId: "" };
7167
+ return __spreadProps(__spreadValues({}, prev), {
7168
+ [lastKey]: __spreadProps(__spreadValues({}, existingEntry), {
7169
+ content: `Error: ${errorMessage}`,
7170
+ callId: lastCallId || existingEntry.callId || ""
7171
+ })
7172
+ });
7173
+ });
5159
7174
  }
5160
7175
  }
5161
7176
  setIsLoading(false);
@@ -5286,47 +7301,220 @@ var AIChatPanel = ({
5286
7301
  );
5287
7302
  }
5288
7303
  }), [CodeBlock, AgentSuggestionCard]);
5289
- const renderThinkingBlocks = useCallback2((isStreaming = false) => {
5290
- const hasActiveBlock = activeThinkingBlock !== null;
5291
- const hasCompletedBlocks = thinkingBlocks.length > 0;
5292
- if (!hasActiveBlock && !hasCompletedBlocks) return null;
5293
- const handleToggleCollapse = (blockKey) => {
5294
- setCollapsedBlocks((prev) => {
5295
- const next = new Set(prev);
5296
- if (next.has(blockKey)) {
5297
- next.delete(blockKey);
5298
- } else {
5299
- next.add(blockKey);
7304
+ const toggleThinkingBlockCollapsed = useCallback2((entryKey, blockKey) => {
7305
+ const collapseKey = getThinkingBlockCollapseKey(entryKey, blockKey);
7306
+ setCollapsedBlocks((prev) => {
7307
+ const next = new Set(prev);
7308
+ if (next.has(collapseKey)) {
7309
+ next.delete(collapseKey);
7310
+ } else {
7311
+ next.add(collapseKey);
7312
+ }
7313
+ return next;
7314
+ });
7315
+ }, [getThinkingBlockCollapseKey]);
7316
+ const renderThinkingBlockCard = (entryKey, block, blockKey, renderKey, isStreaming) => {
7317
+ const collapseKey = getThinkingBlockCollapseKey(entryKey, blockKey);
7318
+ return /* @__PURE__ */ React14.createElement(
7319
+ ThinkingBlock,
7320
+ {
7321
+ key: renderKey,
7322
+ type: block.type,
7323
+ content: block.content,
7324
+ isStreaming,
7325
+ isCollapsed: collapsedBlocks.has(collapseKey),
7326
+ onToggleCollapse: () => toggleThinkingBlockCollapsed(entryKey, blockKey)
7327
+ }
7328
+ );
7329
+ };
7330
+ const renderActiveThinkingBlock = (entryKey, activeBlock, keyPrefix) => {
7331
+ if (!activeBlock) return null;
7332
+ return /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-inline-thinking-events" }, renderThinkingBlockCard(entryKey, activeBlock, "active", `${keyPrefix}-active`, true));
7333
+ };
7334
+ const panelClasses = ["ai-chat-panel", theme === "dark" ? "dark-theme" : ""].filter(Boolean).join(" ");
7335
+ const getToolStatusRank = (status) => {
7336
+ switch (status) {
7337
+ case "error":
7338
+ return 4;
7339
+ case "completed":
7340
+ return 3;
7341
+ case "running":
7342
+ return 2;
7343
+ case "pending":
7344
+ default:
7345
+ return 1;
7346
+ }
7347
+ };
7348
+ const formatToolCallId = (callId) => {
7349
+ const normalized = String(callId || "").trim();
7350
+ if (normalized.length <= 22) return normalized;
7351
+ return `${normalized.slice(0, 10)}...${normalized.slice(-8)}`;
7352
+ };
7353
+ const renderMarkdownContent = (content, key) => {
7354
+ if (!content || !content.trim()) return null;
7355
+ if (markdownClass) {
7356
+ return /* @__PURE__ */ React14.createElement("div", { key, className: markdownClass }, /* @__PURE__ */ React14.createElement(
7357
+ ReactMarkdown2,
7358
+ {
7359
+ remarkPlugins: [remarkGfm2],
7360
+ rehypePlugins: [rehypeRaw2],
7361
+ components: markdownComponents
7362
+ },
7363
+ content
7364
+ ));
7365
+ }
7366
+ return /* @__PURE__ */ React14.createElement(
7367
+ ReactMarkdown2,
7368
+ {
7369
+ key,
7370
+ remarkPlugins: [remarkGfm2],
7371
+ rehypePlugins: [rehypeRaw2],
7372
+ components: markdownComponents
7373
+ },
7374
+ content
7375
+ );
7376
+ };
7377
+ const renderToolStatusRow = (toolStatusRow, key) => /* @__PURE__ */ React14.createElement(
7378
+ "div",
7379
+ {
7380
+ key,
7381
+ className: `ai-chat-tool-status-row ai-chat-tool-status-row--${toolStatusRow.status}`
7382
+ },
7383
+ /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-tool-status-row__main" }, /* @__PURE__ */ React14.createElement(ToolIcon, null), /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-tool-status-row__label" }, toolStatusRow.statusLabel), /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-tool-status-row__call-id" }, formatToolCallId(toolStatusRow.callId))),
7384
+ toolStatusRow.status === "pending" && /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-tool-status-row__actions" }, /* @__PURE__ */ React14.createElement(
7385
+ Button,
7386
+ {
7387
+ size: "sm",
7388
+ variant: "ghost",
7389
+ className: "ai-chat-tool-status-row__button",
7390
+ onClick: () => handleToolApproval(toolStatusRow.toolName, "once")
7391
+ },
7392
+ "once"
7393
+ ), /* @__PURE__ */ React14.createElement(
7394
+ Button,
7395
+ {
7396
+ size: "sm",
7397
+ variant: "ghost",
7398
+ className: "ai-chat-tool-status-row__button",
7399
+ onClick: () => handleToolApproval(toolStatusRow.toolName, "session")
7400
+ },
7401
+ "session"
7402
+ ), /* @__PURE__ */ React14.createElement(
7403
+ Button,
7404
+ {
7405
+ size: "sm",
7406
+ variant: "ghost",
7407
+ className: "ai-chat-tool-status-row__button",
7408
+ onClick: () => handleToolApproval(toolStatusRow.toolName, "always")
7409
+ },
7410
+ "always"
7411
+ ))
7412
+ );
7413
+ const renderContentWithInlineToolCards = (content, toolStatusRows, thinkingBlocksForEntry, entryKey, keyPrefix) => {
7414
+ const { parts, markers } = parseInlineToolMarkers(content);
7415
+ const thinkingBlocksBySignature = /* @__PURE__ */ new Map();
7416
+ thinkingBlocksForEntry.forEach((block, index) => {
7417
+ const signature = String((block == null ? void 0 : block.signature) || "").trim() || getThinkingBlockRenderKey(block, index);
7418
+ if (!thinkingBlocksBySignature.has(signature)) {
7419
+ thinkingBlocksBySignature.set(signature, __spreadProps(__spreadValues({}, block), {
7420
+ signature
7421
+ }));
7422
+ }
7423
+ });
7424
+ const pendingBySignature = /* @__PURE__ */ new Map();
7425
+ const pendingByCallId = /* @__PURE__ */ new Map();
7426
+ toolStatusRows.forEach((row) => {
7427
+ if (!pendingBySignature.has(row.signature)) {
7428
+ pendingBySignature.set(row.signature, row);
7429
+ }
7430
+ if (!pendingByCallId.has(row.callId)) {
7431
+ pendingByCallId.set(row.callId, row);
7432
+ }
7433
+ });
7434
+ const nodes = [];
7435
+ parts.forEach((part, partIndex) => {
7436
+ const { parts: thinkingParts, markers: thinkingMarkers } = parseInlineThinkingMarkers(part);
7437
+ thinkingParts.forEach((thinkingPart, thinkingIndex) => {
7438
+ const markdownNode = renderMarkdownContent(
7439
+ thinkingPart,
7440
+ `${keyPrefix}-md-${partIndex}-${thinkingIndex}`
7441
+ );
7442
+ if (markdownNode) {
7443
+ nodes.push(markdownNode);
5300
7444
  }
5301
- return next;
7445
+ const thinkingMarker = thinkingMarkers[thinkingIndex];
7446
+ if (!thinkingMarker) return;
7447
+ const matchedBlock = thinkingBlocksBySignature.get(thinkingMarker.signature);
7448
+ if (!matchedBlock) return;
7449
+ const blockKey = getThinkingBlockRenderKey(matchedBlock, thinkingIndex);
7450
+ nodes.push(
7451
+ /* @__PURE__ */ React14.createElement(
7452
+ "div",
7453
+ {
7454
+ key: `${keyPrefix}-thinking-${partIndex}-${thinkingIndex}-${blockKey}`,
7455
+ className: "ai-chat-inline-thinking-events"
7456
+ },
7457
+ renderThinkingBlockCard(
7458
+ entryKey,
7459
+ matchedBlock,
7460
+ blockKey,
7461
+ `${keyPrefix}-thinking-card-${partIndex}-${thinkingIndex}-${blockKey}`,
7462
+ false
7463
+ )
7464
+ )
7465
+ );
5302
7466
  });
5303
- };
5304
- return /* @__PURE__ */ React14.createElement(React14.Fragment, null, thinkingBlocks.map((block, index) => {
5305
- const blockKey = `block-${index}`;
5306
- return /* @__PURE__ */ React14.createElement(
5307
- ThinkingBlock,
5308
- {
5309
- key: blockKey,
5310
- type: block.type,
5311
- content: block.content,
5312
- isStreaming: false,
5313
- isCollapsed: collapsedBlocks.has(blockKey),
5314
- onToggleCollapse: () => handleToggleCollapse(blockKey)
7467
+ const marker = markers[partIndex];
7468
+ if (!marker) return;
7469
+ const markerSignature = getToolCallSignature(marker.toolName, marker.callId);
7470
+ const matchedRow = (markerSignature ? pendingBySignature.get(markerSignature) : void 0) || pendingByCallId.get(marker.callId);
7471
+ const fallbackSignature = markerSignature || getToolCallSignature(marker.toolName, `${marker.callId}-${partIndex + 1}`) || `${marker.toolName}::${marker.callId}::${partIndex}`;
7472
+ const rowToRender = matchedRow || {
7473
+ signature: fallbackSignature,
7474
+ toolName: marker.toolName,
7475
+ callId: marker.callId,
7476
+ status: "running",
7477
+ statusLabel: `tool call ${marker.toolName} running`
7478
+ };
7479
+ if (matchedRow) {
7480
+ pendingBySignature.delete(matchedRow.signature);
7481
+ const pendingByCallIdMatch = pendingByCallId.get(matchedRow.callId);
7482
+ if ((pendingByCallIdMatch == null ? void 0 : pendingByCallIdMatch.signature) === matchedRow.signature) {
7483
+ pendingByCallId.delete(matchedRow.callId);
5315
7484
  }
5316
- );
5317
- }), activeThinkingBlock && /* @__PURE__ */ React14.createElement(
5318
- ThinkingBlock,
5319
- {
5320
- key: "active-streaming",
5321
- type: activeThinkingBlock.type,
5322
- content: activeThinkingBlock.content,
5323
- isStreaming: true,
5324
- isCollapsed: collapsedBlocks.has("active"),
5325
- onToggleCollapse: () => handleToggleCollapse("active")
5326
7485
  }
5327
- ));
5328
- }, [thinkingBlocks, activeThinkingBlock, collapsedBlocks]);
5329
- const panelClasses = ["ai-chat-panel", theme === "dark" ? "dark-theme" : ""].filter(Boolean).join(" ");
7486
+ nodes.push(
7487
+ /* @__PURE__ */ React14.createElement(
7488
+ "div",
7489
+ {
7490
+ key: `${keyPrefix}-inline-${rowToRender.signature}-${partIndex}`,
7491
+ className: "ai-chat-inline-tool-events",
7492
+ role: "status",
7493
+ "aria-live": "polite"
7494
+ },
7495
+ renderToolStatusRow(rowToRender, `${keyPrefix}-row-${rowToRender.signature}-${partIndex}`)
7496
+ )
7497
+ );
7498
+ });
7499
+ const unmatchedRows = Array.from(pendingBySignature.values());
7500
+ if (unmatchedRows.length > 0) {
7501
+ nodes.push(
7502
+ /* @__PURE__ */ React14.createElement(
7503
+ "div",
7504
+ {
7505
+ key: `${keyPrefix}-tail`,
7506
+ className: "ai-chat-inline-tool-events ai-chat-inline-tool-events--tail",
7507
+ role: "status",
7508
+ "aria-live": "polite"
7509
+ },
7510
+ unmatchedRows.map(
7511
+ (row, rowIndex) => renderToolStatusRow(row, `${keyPrefix}-tail-${row.signature}-${rowIndex}`)
7512
+ )
7513
+ )
7514
+ );
7515
+ }
7516
+ return /* @__PURE__ */ React14.createElement(React14.Fragment, null, nodes);
7517
+ };
5330
7518
  return /* @__PURE__ */ React14.createElement(
5331
7519
  "div",
5332
7520
  {
@@ -5359,47 +7547,114 @@ var AIChatPanel = ({
5359
7547
  initialMessage
5360
7548
  ))), Object.entries(history).map(([prompt, entry], index, entries) => {
5361
7549
  const isLastEntry = index === entries.length - 1;
7550
+ const isStreamingEntry = isLastEntry && (isLoading || !idle) && !justReset;
7551
+ const continuationAppendBase = isStreamingEntry && activeStreamAppendBaseRef.current && activeStreamAppendBaseRef.current.key === prompt && activeStreamAppendBaseRef.current.base.trim().length > 0 ? activeStreamAppendBaseRef.current : null;
7552
+ const isContinuationStreamingEntry = !!continuationAppendBase;
5362
7553
  const isSystemMessage = prompt.startsWith("__system__:");
5363
7554
  const { cleanedText } = processThinkingTags(entry.content);
5364
7555
  const processedContent = processActions(cleanedText);
5365
- return /* @__PURE__ */ React14.createElement("div", { key: index, className: "ai-chat-entry" }, !(hideInitialPrompt && index === 0) && !isSystemMessage && /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-message ai-chat-message--user" }, /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-message__content" }, formatPromptForDisplay(prompt))), /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-message ai-chat-message--assistant" }, /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-message__content" }, isLastEntry && (isLoading || !idle) && !justReset ? (() => {
5366
- const { cleanedText: streamingCleanedText } = processThinkingTags(response || "");
5367
- const streamingContent = processActions(streamingCleanedText);
7556
+ const entryThinkingBlocks = thinkingBlocksByKey[prompt] || [];
7557
+ const statusBySignature = /* @__PURE__ */ new Map();
7558
+ const upsertToolStatus = (row) => {
7559
+ const existing = statusBySignature.get(row.signature);
7560
+ if (!existing || getToolStatusRank(row.status) >= getToolStatusRank(existing.status)) {
7561
+ statusBySignature.set(row.signature, row);
7562
+ }
7563
+ };
7564
+ const entryToolCalls = Array.isArray(entry.toolCalls) ? entry.toolCalls : [];
7565
+ const entryToolResponses = Array.isArray(entry.toolResponses) ? entry.toolResponses : [];
7566
+ entryToolCalls.forEach((toolCall, toolIndex) => {
7567
+ const toolName = String((toolCall == null ? void 0 : toolCall.name) || "").trim() || "tool";
7568
+ const callId = String((toolCall == null ? void 0 : toolCall.id) || "").trim() || `${toolName}-${toolIndex + 1}`;
7569
+ const signature = getToolCallSignature(toolName, callId) || `completed-${prompt}-${toolIndex}-${toolName}-${callId}`;
7570
+ const matchedResponse = entryToolResponses.find((response2) => String((response2 == null ? void 0 : response2.tool_call_id) || "").trim() === callId) || entryToolResponses[toolIndex];
7571
+ const isError = Boolean(matchedResponse == null ? void 0 : matchedResponse.isError);
7572
+ upsertToolStatus({
7573
+ signature,
7574
+ toolName,
7575
+ callId,
7576
+ status: isError ? "error" : "completed",
7577
+ statusLabel: isError ? `tool call ${toolName} errored` : `tool call ${toolName} completed`
7578
+ });
7579
+ });
7580
+ if (isLastEntry && !justReset) {
7581
+ pendingToolRequests.forEach((request, requestIndex) => {
7582
+ const toolName = String((request == null ? void 0 : request.toolName) || "").trim() || "tool";
7583
+ const callId = String((request == null ? void 0 : request.callId) || "").trim() || `${toolName}-pending-${requestIndex + 1}`;
7584
+ const signature = getToolCallSignature(toolName, callId) || `pending-${prompt}-${requestIndex}-${toolName}-${callId}`;
7585
+ upsertToolStatus({
7586
+ signature,
7587
+ toolName,
7588
+ callId,
7589
+ status: "pending",
7590
+ statusLabel: `tool call ${toolName} awaiting approval`
7591
+ });
7592
+ });
7593
+ activeToolCalls.forEach((activeToolCall, activeIndex) => {
7594
+ const toolName = String((activeToolCall == null ? void 0 : activeToolCall.toolName) || "").trim() || "tool";
7595
+ const callId = String((activeToolCall == null ? void 0 : activeToolCall.callId) || "").trim() || `${toolName}-running-${activeIndex + 1}`;
7596
+ const signature = getToolCallSignature(toolName, callId) || `running-${prompt}-${activeIndex}-${toolName}-${callId}`;
7597
+ upsertToolStatus({
7598
+ signature,
7599
+ toolName,
7600
+ callId,
7601
+ status: "running",
7602
+ statusLabel: `tool call ${toolName} running`
7603
+ });
7604
+ });
7605
+ }
7606
+ const entryToolStatusRows = Array.from(statusBySignature.values());
7607
+ const hasInFlightToolStatus = entryToolStatusRows.some(
7608
+ (row) => row.status === "pending" || row.status === "running"
7609
+ );
7610
+ const isActivePromptEntry = !!lastKey && prompt === lastKey;
7611
+ const shouldShowBusyGapThinkingFallback = isActivePromptEntry && !isStreamingEntry && (isLoading || !idle) && !activeThinkingBlock && !hasInFlightToolStatus;
7612
+ return /* @__PURE__ */ React14.createElement("div", { key: prompt, className: "ai-chat-entry" }, !(hideInitialPrompt && index === 0) && !isSystemMessage && /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-message ai-chat-message--user" }, /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-message__content" }, formatPromptForDisplay(prompt))), /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-message ai-chat-message--assistant" }, /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-message__content" }, isStreamingEntry ? (() => {
7613
+ if (isContinuationStreamingEntry) {
7614
+ const continuationResponseWithInlineToolLabels = formatToolRequestsForDisplay(response || "");
7615
+ const { cleanedText: continuationCleanedText } = processThinkingTags(
7616
+ continuationResponseWithInlineToolLabels
7617
+ );
7618
+ const appendBase2 = continuationAppendBase || activeStreamAppendBaseRef.current;
7619
+ const continuationMergedText = appendBase2 && appendBase2.key === prompt ? mergeContinuationResponseText(appendBase2.base, continuationCleanedText.trim()) : continuationCleanedText;
7620
+ const continuationDisplaySource = continuationMergedText.trim().length > 0 ? continuationMergedText : cleanedText;
7621
+ const continuationDisplayContent = processActions(continuationDisplaySource);
7622
+ const hasVisibleContinuationContent = continuationDisplayContent.trim().length > 0;
7623
+ const hasFreshContinuationContent = continuationCleanedText.trim().length > 0;
7624
+ const showThinkingFallback2 = !activeThinkingBlock && !hasFreshContinuationContent && !hasInFlightToolStatus;
7625
+ return /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-streaming" }, renderActiveThinkingBlock(prompt, activeThinkingBlock, `${prompt}-continuation`), hasVisibleContinuationContent ? /* @__PURE__ */ React14.createElement(React14.Fragment, null, renderContentWithInlineToolCards(
7626
+ continuationDisplayContent,
7627
+ entryToolStatusRows,
7628
+ entryThinkingBlocks,
7629
+ prompt,
7630
+ `${prompt}-continuation`
7631
+ ), showThinkingFallback2 && /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-loading ai-chat-loading--inline" }, /* @__PURE__ */ React14.createElement("span", null, "Thinking"), /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-loading__dots" }, /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-loading__dot" }), /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-loading__dot" }), /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-loading__dot" })))) : /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-loading" }, /* @__PURE__ */ React14.createElement("span", null, "Continuing response"), /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-loading__dots" }, /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-loading__dot" }), /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-loading__dot" }), /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-loading__dot" }))));
7632
+ }
7633
+ const streamingResponseWithInlineToolLabels = formatToolRequestsForDisplay(response || "");
7634
+ const { cleanedText: streamingCleanedText } = processThinkingTags(streamingResponseWithInlineToolLabels);
7635
+ const appendBase = activeStreamAppendBaseRef.current;
7636
+ const streamingMergedText = appendBase && appendBase.key === prompt ? mergeContinuationResponseText(appendBase.base, streamingCleanedText.trim()) : streamingCleanedText;
7637
+ const streamingContent = processActions(streamingMergedText);
5368
7638
  const hasStreamingContent = streamingContent.trim().length > 0;
5369
- return /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-streaming" }, (thinkingBlocks.length > 0 || activeThinkingBlock) && renderThinkingBlocks(true), hasStreamingContent ? markdownClass ? /* @__PURE__ */ React14.createElement("div", { className: markdownClass }, /* @__PURE__ */ React14.createElement(
5370
- ReactMarkdown2,
5371
- {
5372
- remarkPlugins: [remarkGfm2],
5373
- rehypePlugins: [rehypeRaw2],
5374
- components: markdownComponents
5375
- },
5376
- streamingContent
5377
- )) : /* @__PURE__ */ React14.createElement(
5378
- ReactMarkdown2,
5379
- {
5380
- remarkPlugins: [remarkGfm2],
5381
- rehypePlugins: [rehypeRaw2],
5382
- components: markdownComponents
5383
- },
5384
- streamingContent
5385
- ) : /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-loading" }, /* @__PURE__ */ React14.createElement("span", null, thinkingBlocks.length > 0 || activeThinkingBlock ? "Still thinking" : "Thinking"), /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-loading__dots" }, /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-loading__dot" }), /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-loading__dot" }), /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-loading__dot" }))));
5386
- })() : /* @__PURE__ */ React14.createElement(React14.Fragment, null, isLastEntry && thinkingBlocks.length > 0 && renderThinkingBlocks(false), markdownClass ? /* @__PURE__ */ React14.createElement("div", { className: markdownClass }, /* @__PURE__ */ React14.createElement(
5387
- ReactMarkdown2,
5388
- {
5389
- remarkPlugins: [remarkGfm2],
5390
- rehypePlugins: [rehypeRaw2],
5391
- components: markdownComponents
5392
- },
5393
- processedContent
5394
- )) : /* @__PURE__ */ React14.createElement(
5395
- ReactMarkdown2,
5396
- {
5397
- remarkPlugins: [remarkGfm2],
5398
- rehypePlugins: [rehypeRaw2],
5399
- components: markdownComponents
5400
- },
5401
- processedContent
5402
- ))), (!isLastEntry || !isLoading) && /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-message__actions" }, /* @__PURE__ */ React14.createElement(
7639
+ const hasFreshStreamingContent = streamingCleanedText.trim().length > 0;
7640
+ const fallbackHistoryContent = processedContent.trim();
7641
+ const streamingDisplayContent = hasStreamingContent ? streamingContent : fallbackHistoryContent;
7642
+ const hasDisplayContent = streamingDisplayContent.trim().length > 0;
7643
+ const showThinkingFallback = !activeThinkingBlock && !hasFreshStreamingContent && !hasInFlightToolStatus;
7644
+ return /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-streaming" }, renderActiveThinkingBlock(prompt, activeThinkingBlock, `${prompt}-streaming`), hasDisplayContent ? /* @__PURE__ */ React14.createElement(React14.Fragment, null, renderContentWithInlineToolCards(
7645
+ streamingDisplayContent,
7646
+ entryToolStatusRows,
7647
+ entryThinkingBlocks,
7648
+ prompt,
7649
+ `${prompt}-streaming`
7650
+ ), showThinkingFallback && /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-loading ai-chat-loading--inline" }, /* @__PURE__ */ React14.createElement("span", null, "Thinking"), /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-loading__dots" }, /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-loading__dot" }), /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-loading__dot" }), /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-loading__dot" })))) : /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-loading" }, /* @__PURE__ */ React14.createElement("span", null, activeThinkingBlock ? "Still thinking" : "Thinking"), /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-loading__dots" }, /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-loading__dot" }), /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-loading__dot" }), /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-loading__dot" }))));
7651
+ })() : /* @__PURE__ */ React14.createElement(React14.Fragment, null, renderContentWithInlineToolCards(
7652
+ processedContent,
7653
+ entryToolStatusRows,
7654
+ entryThinkingBlocks,
7655
+ prompt,
7656
+ `${prompt}-final`
7657
+ ), shouldShowBusyGapThinkingFallback && /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-loading ai-chat-loading--inline", "aria-live": "polite" }, /* @__PURE__ */ React14.createElement("span", null, "Thinking"), /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-loading__dots" }, /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-loading__dot" }), /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-loading__dot" }), /* @__PURE__ */ React14.createElement("span", { className: "ai-chat-loading__dot" }))))), (!isLastEntry || !isLoading) && /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-message__actions" }, /* @__PURE__ */ React14.createElement(
5403
7658
  "button",
5404
7659
  {
5405
7660
  className: "ai-chat-action-button",
@@ -5425,7 +7680,7 @@ var AIChatPanel = ({
5425
7680
  style: (feedbackCallId == null ? void 0 : feedbackCallId.callId) === entry.callId && (feedbackCallId == null ? void 0 : feedbackCallId.type) === "down" ? { color: "#ef4444" } : void 0
5426
7681
  },
5427
7682
  (feedbackCallId == null ? void 0 : feedbackCallId.callId) === entry.callId && (feedbackCallId == null ? void 0 : feedbackCallId.type) === "down" ? /* @__PURE__ */ React14.createElement("span", { style: { fontSize: "11px", fontWeight: 500 } }, "Thanks!") : /* @__PURE__ */ React14.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__ */ React14.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" }))
5428
- ), (entry.toolCalls || entry.toolResponses) && /* @__PURE__ */ React14.createElement(
7683
+ ), /* @__PURE__ */ React14.createElement(
5429
7684
  "button",
5430
7685
  {
5431
7686
  className: "ai-chat-action-button",
@@ -5451,34 +7706,6 @@ var AIChatPanel = ({
5451
7706
  },
5452
7707
  question
5453
7708
  ))), /* @__PURE__ */ React14.createElement("div", { ref: bottomRef })),
5454
- pendingToolRequests.length > 0 && /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-approve-tools-panel" }, /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-approve-tools-header" }, "Tool Approval Required"), /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-approve-tools-description" }, "The AI wants to use the following tools:"), getUniqueToolNames().map((toolName) => /* @__PURE__ */ React14.createElement("div", { key: toolName, className: "ai-chat-approve-tool-item" }, /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-approve-tool-name" }, toolName), /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-approve-tools-buttons" }, /* @__PURE__ */ React14.createElement(
5455
- Button,
5456
- {
5457
- size: "sm",
5458
- variant: "outline",
5459
- className: "ai-chat-approve-tools-button",
5460
- onClick: () => handleToolApproval(toolName, "once")
5461
- },
5462
- "Once"
5463
- ), /* @__PURE__ */ React14.createElement(
5464
- Button,
5465
- {
5466
- size: "sm",
5467
- variant: "outline",
5468
- className: "ai-chat-approve-tools-button",
5469
- onClick: () => handleToolApproval(toolName, "session")
5470
- },
5471
- "This Session"
5472
- ), /* @__PURE__ */ React14.createElement(
5473
- Button,
5474
- {
5475
- size: "sm",
5476
- variant: "default",
5477
- className: "ai-chat-approve-tools-button",
5478
- onClick: () => handleToolApproval(toolName, "always")
5479
- },
5480
- "Always"
5481
- ))))),
5482
7709
  (showSaveButton || showEmailButton || showCallToAction) && /* @__PURE__ */ React14.createElement("div", { className: "ai-chat-button-container" }, showSaveButton && /* @__PURE__ */ React14.createElement(
5483
7710
  Button,
5484
7711
  {
@@ -5614,7 +7841,7 @@ var AIChatPanel = ({
5614
7841
  ChatInput,
5615
7842
  {
5616
7843
  placeholder,
5617
- idle,
7844
+ isBusy: isLoading || !idle,
5618
7845
  onSubmit: continueChat,
5619
7846
  onStop: handleStop,
5620
7847
  agentOptions,
@@ -5861,13 +8088,14 @@ var SparkleIcon = () => /* @__PURE__ */ React15.createElement("svg", { width: "1
5861
8088
  var normalizeConversationListPayload = (payload) => {
5862
8089
  if (!payload) return [];
5863
8090
  const conversations = Array.isArray(payload) ? payload : payload.conversations || [];
5864
- return conversations.map((conv) => {
8091
+ const normalized = conversations.map((conv, index) => {
5865
8092
  let title = conv.title && conv.title !== "Conversation" ? conv.title : "";
5866
8093
  if (!title) {
5867
8094
  title = conv.summary || extractTitleFromConversation(conv);
5868
8095
  }
8096
+ const resolvedConversationId = conv.conversationId || conv.id || conv.conversation_id || `conversation-${index}-${Date.now()}`;
5869
8097
  return {
5870
- conversationId: conv.conversationId || conv.id || conv.conversation_id,
8098
+ conversationId: resolvedConversationId,
5871
8099
  title,
5872
8100
  summary: conv.summary,
5873
8101
  createdAt: conv.createdAt || conv.created_at || conv.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
@@ -5876,6 +8104,20 @@ var normalizeConversationListPayload = (payload) => {
5876
8104
  messageCount: conv.messageCount || conv.message_count
5877
8105
  };
5878
8106
  });
8107
+ const dedupedById = /* @__PURE__ */ new Map();
8108
+ for (const conv of normalized) {
8109
+ const existing = dedupedById.get(conv.conversationId);
8110
+ if (!existing) {
8111
+ dedupedById.set(conv.conversationId, conv);
8112
+ continue;
8113
+ }
8114
+ const existingUpdated = new Date(existing.updatedAt).getTime();
8115
+ const incomingUpdated = new Date(conv.updatedAt).getTime();
8116
+ if (incomingUpdated >= existingUpdated) {
8117
+ dedupedById.set(conv.conversationId, conv);
8118
+ }
8119
+ }
8120
+ return Array.from(dedupedById.values());
5879
8121
  };
5880
8122
  var extractTitleFromConversation = (conv) => {
5881
8123
  var _a;
@@ -5949,6 +8191,248 @@ var groupConversationsByTime = (conversations, showAllGroups = false) => {
5949
8191
  });
5950
8192
  return Object.entries(groups).filter(([_, convs]) => showAllGroups || convs.length > 0).map(([label, conversations2]) => ({ label, conversations: conversations2, count: conversations2.length }));
5951
8193
  };
8194
+ var toRecord = (value) => {
8195
+ if (typeof value === "object" && value !== null) {
8196
+ return value;
8197
+ }
8198
+ return null;
8199
+ };
8200
+ var toStringValue = (value) => {
8201
+ if (typeof value === "string") return value;
8202
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
8203
+ return "";
8204
+ };
8205
+ var normalizeContentText = (value) => {
8206
+ if (typeof value === "string") return value;
8207
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
8208
+ if (Array.isArray(value)) {
8209
+ return value.map((item) => normalizeContentText(item)).filter(Boolean).join("\n").trim();
8210
+ }
8211
+ const record = toRecord(value);
8212
+ if (!record) return "";
8213
+ const directText = toStringValue(record.text);
8214
+ if (directText) return directText;
8215
+ const directValue = toStringValue(record.value);
8216
+ if (directValue) return directValue;
8217
+ const directContent = toStringValue(record.content);
8218
+ if (directContent) return directContent;
8219
+ if (Array.isArray(record.content)) {
8220
+ return normalizeContentText(record.content);
8221
+ }
8222
+ if (Array.isArray(record.parts)) {
8223
+ return normalizeContentText(record.parts);
8224
+ }
8225
+ if (record.content && typeof record.content === "object") {
8226
+ return normalizeContentText(record.content);
8227
+ }
8228
+ return "";
8229
+ };
8230
+ var parseCallMessages = (rawMessages) => {
8231
+ var _a, _b;
8232
+ let parsed = rawMessages;
8233
+ if (typeof rawMessages === "string") {
8234
+ try {
8235
+ parsed = JSON.parse(rawMessages);
8236
+ } catch (e) {
8237
+ return [];
8238
+ }
8239
+ }
8240
+ const parsedRecord = toRecord(parsed);
8241
+ if (parsedRecord && Array.isArray(parsedRecord.messages)) {
8242
+ parsed = parsedRecord.messages;
8243
+ }
8244
+ if (!Array.isArray(parsed)) {
8245
+ return [];
8246
+ }
8247
+ const normalized = [];
8248
+ for (const message of parsed) {
8249
+ const messageRecord = toRecord(message);
8250
+ if (!messageRecord) continue;
8251
+ const role = toStringValue(messageRecord.role).toLowerCase().trim();
8252
+ if (!role) continue;
8253
+ const content = normalizeContentText(
8254
+ (_b = (_a = messageRecord.content) != null ? _a : messageRecord.text) != null ? _b : messageRecord.message
8255
+ ).trim();
8256
+ if (!content) continue;
8257
+ normalized.push({ role, content });
8258
+ }
8259
+ return normalized;
8260
+ };
8261
+ var shouldSkipTranscriptMessage = (message) => {
8262
+ return message.role === "system" || message.role === "user" && message.content.startsWith("__system__:");
8263
+ };
8264
+ var parseTimestampMs = (value) => {
8265
+ const timestamp = toStringValue(value).trim();
8266
+ if (!timestamp) return null;
8267
+ const parsed = Date.parse(timestamp);
8268
+ return Number.isFinite(parsed) ? parsed : null;
8269
+ };
8270
+ var getCallTimestampMs = (call, fallbackIndex) => {
8271
+ const timestampCandidates = [
8272
+ call.createdAt,
8273
+ call.created_at,
8274
+ call.timestamp,
8275
+ call.updatedAt,
8276
+ call.updated_at
8277
+ ];
8278
+ for (const candidate of timestampCandidates) {
8279
+ const parsed = parseTimestampMs(candidate);
8280
+ if (parsed !== null) return parsed;
8281
+ }
8282
+ return fallbackIndex;
8283
+ };
8284
+ var getCallId = (call) => {
8285
+ const id = toStringValue(call.id);
8286
+ if (id) return id;
8287
+ return toStringValue(call.callId);
8288
+ };
8289
+ var normalizeCallsPayload = (payload) => {
8290
+ const payloadRecord = toRecord(payload);
8291
+ const calls = Array.isArray(payload) ? payload : payloadRecord && Array.isArray(payloadRecord.calls) ? payloadRecord.calls : [];
8292
+ return calls.map((call) => toRecord(call)).filter((call) => call !== null);
8293
+ };
8294
+ var turnsEqual = (left, right) => {
8295
+ return left.prompt === right.prompt && left.response === right.response;
8296
+ };
8297
+ var getPrefixMatchLength = (existing, incoming) => {
8298
+ const max = Math.min(existing.length, incoming.length);
8299
+ let matched = 0;
8300
+ while (matched < max && turnsEqual(existing[matched], incoming[matched])) {
8301
+ matched += 1;
8302
+ }
8303
+ return matched;
8304
+ };
8305
+ var getSuffixPrefixOverlap = (existing, incoming) => {
8306
+ const max = Math.min(existing.length, incoming.length);
8307
+ for (let overlap = max; overlap > 0; overlap -= 1) {
8308
+ let matches = true;
8309
+ for (let idx = 0; idx < overlap; idx += 1) {
8310
+ const left = existing[existing.length - overlap + idx];
8311
+ const right = incoming[idx];
8312
+ if (!turnsEqual(left, right)) {
8313
+ matches = false;
8314
+ break;
8315
+ }
8316
+ }
8317
+ if (matches) return overlap;
8318
+ }
8319
+ return 0;
8320
+ };
8321
+ var mergeTranscriptTurns = (existing, incoming) => {
8322
+ if (incoming.length === 0) return existing;
8323
+ if (existing.length === 0) return [...incoming];
8324
+ const prefixMatchLength = getPrefixMatchLength(existing, incoming);
8325
+ if (prefixMatchLength > 0) {
8326
+ return [...existing, ...incoming.slice(prefixMatchLength)];
8327
+ }
8328
+ const overlap = getSuffixPrefixOverlap(existing, incoming);
8329
+ if (overlap > 0) {
8330
+ return [...existing, ...incoming.slice(overlap)];
8331
+ }
8332
+ return [...existing, ...incoming];
8333
+ };
8334
+ var buildTurnsFromMessages = (messages, fallbackResponse, callId, timestampMs) => {
8335
+ const turns = [];
8336
+ for (let index = 0; index < messages.length; index += 1) {
8337
+ const message = messages[index];
8338
+ if (!message || message.role !== "user") continue;
8339
+ const prompt = message.content.trim();
8340
+ if (!prompt) continue;
8341
+ let response = "";
8342
+ let reachedNextUser = false;
8343
+ for (let lookAhead = index + 1; lookAhead < messages.length; lookAhead += 1) {
8344
+ const nextMessage = messages[lookAhead];
8345
+ if (!nextMessage) continue;
8346
+ if (nextMessage.role === "assistant") {
8347
+ response = nextMessage.content.trim();
8348
+ index = lookAhead;
8349
+ break;
8350
+ }
8351
+ if (nextMessage.role === "user") {
8352
+ reachedNextUser = true;
8353
+ break;
8354
+ }
8355
+ }
8356
+ if (!response && !reachedNextUser) {
8357
+ response = fallbackResponse;
8358
+ }
8359
+ if (!response) continue;
8360
+ turns.push({
8361
+ prompt,
8362
+ response,
8363
+ callId,
8364
+ timestampMs
8365
+ });
8366
+ }
8367
+ return turns;
8368
+ };
8369
+ var buildTranscriptTurnsFromCalls = (calls) => {
8370
+ const orderedCalls = calls.map((call, index) => ({
8371
+ call,
8372
+ index,
8373
+ timestampMs: getCallTimestampMs(call, index)
8374
+ })).sort((left, right) => {
8375
+ if (left.timestampMs === right.timestampMs) return left.index - right.index;
8376
+ return left.timestampMs - right.timestampMs;
8377
+ });
8378
+ let mergedTurns = [];
8379
+ for (const orderedCall of orderedCalls) {
8380
+ const { call, timestampMs } = orderedCall;
8381
+ const callId = getCallId(call);
8382
+ const fallbackResponse = normalizeContentText(call.response).trim();
8383
+ const parsedMessages = parseCallMessages(call.messages).filter(
8384
+ (message) => !shouldSkipTranscriptMessage(message)
8385
+ );
8386
+ let callTurns = buildTurnsFromMessages(
8387
+ parsedMessages,
8388
+ fallbackResponse,
8389
+ callId,
8390
+ timestampMs
8391
+ );
8392
+ if (callTurns.length === 0) {
8393
+ const fallbackPrompt = normalizeContentText(call.prompt).trim();
8394
+ if (fallbackPrompt && fallbackResponse) {
8395
+ callTurns = [
8396
+ {
8397
+ prompt: fallbackPrompt,
8398
+ response: fallbackResponse,
8399
+ callId,
8400
+ timestampMs
8401
+ }
8402
+ ];
8403
+ }
8404
+ }
8405
+ mergedTurns = mergeTranscriptTurns(mergedTurns, callTurns);
8406
+ }
8407
+ return mergedTurns;
8408
+ };
8409
+ var buildHistoryFromTranscriptTurns = (turns) => {
8410
+ const history = {};
8411
+ const keyUsageByPrompt = /* @__PURE__ */ new Map();
8412
+ turns.forEach((turn, index) => {
8413
+ var _a;
8414
+ const prompt = turn.prompt.trim();
8415
+ const response = turn.response.trim();
8416
+ if (!prompt || !response) return;
8417
+ const baseTimestampMs = Number.isFinite(turn.timestampMs) ? turn.timestampMs : index;
8418
+ const keySeed = `${baseTimestampMs}:${prompt}`;
8419
+ const keyUsageCount = (_a = keyUsageByPrompt.get(keySeed)) != null ? _a : 0;
8420
+ keyUsageByPrompt.set(keySeed, keyUsageCount + 1);
8421
+ const uniqueTimestamp = new Date(baseTimestampMs + keyUsageCount).toISOString();
8422
+ const historyKey = `${uniqueTimestamp}:${prompt}`;
8423
+ history[historyKey] = {
8424
+ content: response,
8425
+ callId: turn.callId
8426
+ };
8427
+ });
8428
+ return history;
8429
+ };
8430
+ var truncatePromptForTitle = (prompt) => {
8431
+ if (prompt.length > 60) {
8432
+ return `${prompt.slice(0, 57)}...`;
8433
+ }
8434
+ return prompt;
8435
+ };
5952
8436
  var EMPTY_ARRAY = [];
5953
8437
  var EMPTY_HISTORY = {};
5954
8438
  var ChatPanelWrapper = ({
@@ -5982,6 +8466,7 @@ var ChatPanelWrapper = ({
5982
8466
  disabledSectionIds,
5983
8467
  onToggleSection,
5984
8468
  onConversationCreated,
8469
+ onBeforeSend,
5985
8470
  conversationInitialPrompt,
5986
8471
  // New props from ChatPanel port
5987
8472
  cssUrl,
@@ -5998,7 +8483,11 @@ var ChatPanelWrapper = ({
5998
8483
  callToActionEmailAddress,
5999
8484
  callToActionEmailSubject,
6000
8485
  customerEmailCaptureMode,
6001
- customerEmailCapturePlaceholder
8486
+ customerEmailCapturePlaceholder,
8487
+ resolveMcpAuthHeaders,
8488
+ localToolExecutors,
8489
+ traceContextMode,
8490
+ autoApproveTools
6002
8491
  }) => {
6003
8492
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
6004
8493
  const convAgentProfile = getAgent(activeConv.agentId);
@@ -6022,6 +8511,16 @@ var ChatPanelWrapper = ({
6022
8511
  },
6023
8512
  [onConversationCreated, activeConv.conversationId]
6024
8513
  );
8514
+ const beforeSendCallback = useCallback4(
8515
+ (payload) => {
8516
+ if (!onBeforeSend) return;
8517
+ return onBeforeSend(__spreadProps(__spreadValues({}, payload), {
8518
+ conversationId: payload.conversationId || activeConv.conversationId,
8519
+ agentId: payload.agentId || activeConv.agentId
8520
+ }));
8521
+ },
8522
+ [onBeforeSend, activeConv.conversationId, activeConv.agentId]
8523
+ );
6025
8524
  const agentStatus = convAgentProfile == null ? void 0 : convAgentProfile.status;
6026
8525
  const promptsString = (convAgentMetadata == null ? void 0 : convAgentMetadata.displayFollowOnPrompts) || "";
6027
8526
  let effectiveFollowOnQuestions;
@@ -6085,6 +8584,7 @@ var ChatPanelWrapper = ({
6085
8584
  disabledSectionIds,
6086
8585
  onToggleSection,
6087
8586
  onConversationCreated: conversationCreatedCallback,
8587
+ onBeforeSend: beforeSendCallback,
6088
8588
  cssUrl,
6089
8589
  markdownClass,
6090
8590
  width,
@@ -6099,7 +8599,11 @@ var ChatPanelWrapper = ({
6099
8599
  callToActionEmailAddress: (_g = callToActionEmailAddress != null ? callToActionEmailAddress : convAgentMetadata.displayCallToActionEmailAddress) != null ? _g : "",
6100
8600
  callToActionEmailSubject: (_h = callToActionEmailSubject != null ? callToActionEmailSubject : convAgentMetadata.displayCallToActionEmailSubject) != null ? _h : "Agent CTA submitted",
6101
8601
  customerEmailCaptureMode: (_i = customerEmailCaptureMode != null ? customerEmailCaptureMode : convAgentMetadata == null ? void 0 : convAgentMetadata.customerEmailCaptureMode) != null ? _i : "HIDE",
6102
- customerEmailCapturePlaceholder: (_j = customerEmailCapturePlaceholder != null ? customerEmailCapturePlaceholder : convAgentMetadata == null ? void 0 : convAgentMetadata.customerEmailCapturePlaceholder) != null ? _j : "Please enter your email..."
8602
+ customerEmailCapturePlaceholder: (_j = customerEmailCapturePlaceholder != null ? customerEmailCapturePlaceholder : convAgentMetadata == null ? void 0 : convAgentMetadata.customerEmailCapturePlaceholder) != null ? _j : "Please enter your email...",
8603
+ resolveMcpAuthHeaders,
8604
+ localToolExecutors,
8605
+ traceContextMode,
8606
+ autoApproveTools
6103
8607
  }
6104
8608
  )
6105
8609
  );
@@ -6150,6 +8654,7 @@ var AIAgentPanel = React15.forwardRef(({
6150
8654
  enableContextDetailView = false,
6151
8655
  onAgentSwitch,
6152
8656
  onConversationChange,
8657
+ onBeforeSend,
6153
8658
  historyChangedCallback,
6154
8659
  responseCompleteCallback,
6155
8660
  thumbsUpClick,
@@ -6181,7 +8686,11 @@ var AIAgentPanel = React15.forwardRef(({
6181
8686
  callToActionEmailAddress,
6182
8687
  callToActionEmailSubject,
6183
8688
  customerEmailCaptureMode,
6184
- customerEmailCapturePlaceholder
8689
+ customerEmailCapturePlaceholder,
8690
+ resolveMcpAuthHeaders,
8691
+ localToolExecutors,
8692
+ traceContextMode = "standard",
8693
+ autoApproveTools
6185
8694
  }, ref) => {
6186
8695
  var _a, _b, _c, _d;
6187
8696
  useEffect10(() => {
@@ -6201,13 +8710,7 @@ var AIAgentPanel = React15.forwardRef(({
6201
8710
  const [uncontrolledIsCollapsed, setUncontrolledIsCollapsed] = useState9(collapsible && defaultCollapsed);
6202
8711
  const isControlled = controlledIsCollapsed !== void 0;
6203
8712
  const isCollapsed = isControlled ? controlledIsCollapsed : uncontrolledIsCollapsed;
6204
- const [isHistoryCollapsed, setIsHistoryCollapsed] = useState9(() => {
6205
- if (typeof window !== "undefined") {
6206
- const saved = localStorage.getItem("ai-agent-panel-history-collapsed");
6207
- return saved === "true";
6208
- }
6209
- return false;
6210
- });
8713
+ const [isHistoryCollapsed, setIsHistoryCollapsed] = useState9(true);
6211
8714
  const [panelWidth, setPanelWidth] = useState9(() => {
6212
8715
  if (typeof window !== "undefined") {
6213
8716
  const savedWidth = localStorage.getItem("ai-agent-panel-width");
@@ -6309,8 +8812,12 @@ var AIAgentPanel = React15.forwardRef(({
6309
8812
  const [conversationsLoading, setConversationsLoading] = useState9(false);
6310
8813
  const [conversationsError, setConversationsError] = useState9(null);
6311
8814
  const [searchQuery, setSearchQuery] = useState9("");
8815
+ const controlledConversationId = typeof conversation === "string" && conversation.trim() !== "" ? conversation.trim() : null;
8816
+ const isConversationControlled = controlledConversationId !== null;
6312
8817
  const [activeConversations, setActiveConversations] = useState9(/* @__PURE__ */ new Map());
6313
- const [currentConversationId, setCurrentConversationId] = useState9(conversation || null);
8818
+ const [currentConversationId, setCurrentConversationId] = useState9(
8819
+ controlledConversationId
8820
+ );
6314
8821
  const [conversationFirstPrompts, setConversationFirstPrompts] = useState9({});
6315
8822
  const [loadingPrompts, setLoadingPrompts] = useState9(/* @__PURE__ */ new Set());
6316
8823
  const [loadingConversationId, setLoadingConversationId] = useState9(null);
@@ -6330,6 +8837,7 @@ var AIAgentPanel = React15.forwardRef(({
6330
8837
  agentList
6331
8838
  } = useAgentRegistry(agentIds, { url, localOverrides });
6332
8839
  const fetchInProgressRef = useRef7(false);
8840
+ const loadingTranscriptIdsRef = useRef7(/* @__PURE__ */ new Set());
6333
8841
  const lastFetchedAgentRef = useRef7(null);
6334
8842
  const checkedPromptsRef = useRef7(/* @__PURE__ */ new Set());
6335
8843
  const fetchingPromptsRef = useRef7(/* @__PURE__ */ new Set());
@@ -6338,29 +8846,49 @@ var AIAgentPanel = React15.forwardRef(({
6338
8846
  activeConversationsRef.current = activeConversations;
6339
8847
  const currentConversationIdRef = useRef7(currentConversationId);
6340
8848
  currentConversationIdRef.current = currentConversationId;
6341
- React15.useImperativeHandle(ref, () => ({
6342
- startNewConversation: (prompt, agent) => {
6343
- const targetAgent = agent || currentAgentId;
8849
+ const commitConversationSelection = useCallback4(
8850
+ (conversationId, notifyChange) => {
8851
+ const shouldSyncLocalState = !isConversationControlled || !notifyChange || controlledConversationId === conversationId;
8852
+ if (shouldSyncLocalState) {
8853
+ setCurrentConversationId(conversationId);
8854
+ }
8855
+ if (notifyChange && conversationId && onConversationChange) {
8856
+ const shouldNotifyParent = !isConversationControlled || controlledConversationId !== conversationId;
8857
+ if (shouldNotifyParent) {
8858
+ onConversationChange(conversationId);
8859
+ }
8860
+ }
8861
+ },
8862
+ [controlledConversationId, isConversationControlled, onConversationChange]
8863
+ );
8864
+ const createDraftConversation = useCallback4(
8865
+ (agentId, conversationInitialPrompt) => {
6344
8866
  const tempId = `new-${Date.now()}`;
6345
8867
  setActiveConversations((prev) => {
6346
8868
  const next = new Map(prev);
6347
8869
  next.set(tempId, {
6348
8870
  conversationId: tempId,
6349
8871
  stableKey: tempId,
6350
- agentId: targetAgent,
8872
+ agentId,
6351
8873
  history: {},
8874
+ transcriptLoaded: true,
6352
8875
  isLoading: false,
6353
8876
  title: "New conversation",
6354
- conversationInitialPrompt: prompt
8877
+ conversationInitialPrompt
6355
8878
  });
6356
8879
  return next;
6357
8880
  });
6358
- setCurrentConversationId(tempId);
6359
- if (onConversationChange) {
6360
- onConversationChange(tempId);
6361
- }
8881
+ return tempId;
8882
+ },
8883
+ []
8884
+ );
8885
+ React15.useImperativeHandle(ref, () => ({
8886
+ startNewConversation: (prompt, agent) => {
8887
+ const targetAgent = agent || currentAgentId;
8888
+ const tempId = createDraftConversation(targetAgent, prompt);
8889
+ commitConversationSelection(tempId, true);
6362
8890
  }
6363
- }), [currentAgentId, onConversationChange]);
8891
+ }), [commitConversationSelection, createDraftConversation, currentAgentId]);
6364
8892
  const [showContextNotification, setShowContextNotification] = useState9(false);
6365
8893
  const prevContextRef = useRef7(null);
6366
8894
  const contextNotificationTimeoutRef = useRef7(null);
@@ -6502,14 +9030,15 @@ var AIAgentPanel = React15.forwardRef(({
6502
9030
  }
6503
9031
  }
6504
9032
  }), [apiKey, customerId, getAgent, fetchFirstPrompt]);
6505
- const loadConversationTranscript = useCallback4((conversationId, agentIdForConversation, title) => __async(void 0, null, function* () {
9033
+ const loadConversationTranscript = useCallback4((conversationId, agentIdForConversation, title, notifyConversationChange = true) => __async(void 0, null, function* () {
6506
9034
  var _a2;
6507
9035
  const existingActive = activeConversationsRef.current.get(conversationId);
6508
- if (existingActive) {
6509
- setCurrentConversationId(conversationId);
6510
- if (onConversationChange) {
6511
- onConversationChange(conversationId);
6512
- }
9036
+ if (existingActive && (existingActive.transcriptLoaded || Object.keys(existingActive.history || {}).length > 0)) {
9037
+ commitConversationSelection(conversationId, notifyConversationChange);
9038
+ return;
9039
+ }
9040
+ if (loadingTranscriptIdsRef.current.has(conversationId)) {
9041
+ commitConversationSelection(conversationId, notifyConversationChange);
6513
9042
  return;
6514
9043
  }
6515
9044
  const agentIdToUse = agentIdForConversation || currentAgentId;
@@ -6519,6 +9048,8 @@ var AIAgentPanel = React15.forwardRef(({
6519
9048
  setConversationsError("Missing API key or project ID");
6520
9049
  return;
6521
9050
  }
9051
+ loadingTranscriptIdsRef.current.add(conversationId);
9052
+ setConversationsError(null);
6522
9053
  setLoadingConversationId(conversationId);
6523
9054
  try {
6524
9055
  console.log("loadConversationTranscript - conversationId:", conversationId);
@@ -6531,57 +9062,34 @@ var AIAgentPanel = React15.forwardRef(({
6531
9062
  }
6532
9063
  });
6533
9064
  if (!response.ok) {
9065
+ if (response.status === 404) {
9066
+ const conversationTitle2 = title || conversationFirstPrompts[conversationId] || "New conversation";
9067
+ setActiveConversations((prev) => {
9068
+ const next = new Map(prev);
9069
+ next.set(conversationId, {
9070
+ conversationId,
9071
+ stableKey: conversationId,
9072
+ agentId: agentIdToUse,
9073
+ history: {},
9074
+ transcriptLoaded: true,
9075
+ isLoading: false,
9076
+ title: conversationTitle2
9077
+ });
9078
+ return next;
9079
+ });
9080
+ commitConversationSelection(conversationId, notifyConversationChange);
9081
+ return;
9082
+ }
6534
9083
  throw new Error(`Failed to load conversation (${response.status})`);
6535
9084
  }
6536
9085
  const payload = yield response.json();
6537
9086
  console.log("loadConversationTranscript - API response:", payload);
6538
- const history = {};
6539
- let firstPrompt = null;
6540
- if (Array.isArray(payload) && payload.length > 0) {
6541
- const lastCall = payload[payload.length - 1];
6542
- const callId = lastCall.id || "";
6543
- const timestamp = lastCall.createdAt || lastCall.created_at || (/* @__PURE__ */ new Date()).toISOString();
6544
- if (lastCall.messages) {
6545
- try {
6546
- const messages2 = JSON.parse(lastCall.messages);
6547
- console.log("loadConversationTranscript - parsed messages:", messages2);
6548
- const relevantMessages = messages2.filter(
6549
- (msg) => msg.role !== "system" && !(msg.role === "user" && msg.content.startsWith("__system__:"))
6550
- );
6551
- console.log("loadConversationTranscript - filtered messages:", relevantMessages);
6552
- for (let i = 0; i < relevantMessages.length; i++) {
6553
- const msg = relevantMessages[i];
6554
- if (!msg) continue;
6555
- if (msg.role === "user") {
6556
- if (!firstPrompt) {
6557
- firstPrompt = msg.content.length > 60 ? msg.content.slice(0, 57) + "..." : msg.content;
6558
- }
6559
- const nextMsg = relevantMessages[i + 1];
6560
- if (nextMsg && nextMsg.role === "assistant") {
6561
- const historyKey = `${timestamp}:${msg.content}`;
6562
- history[historyKey] = {
6563
- content: nextMsg.content,
6564
- callId
6565
- };
6566
- i++;
6567
- } else {
6568
- if (lastCall.response) {
6569
- const historyKey = `${timestamp}:${msg.content}`;
6570
- history[historyKey] = {
6571
- content: lastCall.response,
6572
- callId
6573
- };
6574
- }
6575
- }
6576
- }
6577
- }
6578
- } catch (err) {
6579
- console.error("loadConversationTranscript - failed to parse messages property:", err);
6580
- }
6581
- }
6582
- }
6583
- console.log("loadConversationTranscript - created", Object.keys(history).length, "history entries");
6584
- console.log("loadConversationTranscript - parsed history:", history);
9087
+ const calls = normalizeCallsPayload(payload);
9088
+ const transcriptTurns = buildTranscriptTurnsFromCalls(calls);
9089
+ const history = buildHistoryFromTranscriptTurns(transcriptTurns);
9090
+ const firstPrompt = transcriptTurns.length > 0 ? truncatePromptForTitle(transcriptTurns[0].prompt) : null;
9091
+ console.log("loadConversationTranscript - parsed calls:", calls.length);
9092
+ console.log("loadConversationTranscript - created history entries:", Object.keys(history).length);
6585
9093
  if (firstPrompt) {
6586
9094
  setConversationFirstPrompts((prev) => __spreadProps(__spreadValues({}, prev), {
6587
9095
  [conversationId]: firstPrompt
@@ -6596,22 +9104,85 @@ var AIAgentPanel = React15.forwardRef(({
6596
9104
  // Use real ID as stable key when loading from API
6597
9105
  agentId: agentIdToUse,
6598
9106
  history,
9107
+ transcriptLoaded: true,
6599
9108
  isLoading: false,
6600
9109
  title: conversationTitle
6601
9110
  });
6602
9111
  return next;
6603
9112
  });
6604
- setCurrentConversationId(conversationId);
6605
- if (onConversationChange) {
6606
- onConversationChange(conversationId);
6607
- }
6608
- setLoadingConversationId(null);
9113
+ commitConversationSelection(conversationId, notifyConversationChange);
6609
9114
  } catch (error) {
6610
9115
  console.error("Failed to load conversation:", error);
6611
9116
  setConversationsError(error.message || "Failed to load conversation");
6612
- setLoadingConversationId(null);
9117
+ } finally {
9118
+ loadingTranscriptIdsRef.current.delete(conversationId);
9119
+ setLoadingConversationId((prev) => prev === conversationId ? null : prev);
9120
+ }
9121
+ }), [apiKey, commitConversationSelection, conversationFirstPrompts, currentAgentId, getAgent]);
9122
+ useEffect10(() => {
9123
+ if (!isConversationControlled) return;
9124
+ const targetConversationId = controlledConversationId;
9125
+ if (!targetConversationId) {
9126
+ if (currentConversationIdRef.current !== null) {
9127
+ setCurrentConversationId(null);
9128
+ }
9129
+ return;
9130
+ }
9131
+ if (targetConversationId.startsWith("new-")) {
9132
+ setActiveConversations((prev) => {
9133
+ if (prev.has(targetConversationId)) {
9134
+ return prev;
9135
+ }
9136
+ const next = new Map(prev);
9137
+ next.set(targetConversationId, {
9138
+ conversationId: targetConversationId,
9139
+ stableKey: targetConversationId,
9140
+ agentId: currentAgentId,
9141
+ history: {},
9142
+ transcriptLoaded: true,
9143
+ isLoading: false,
9144
+ title: "New conversation"
9145
+ });
9146
+ return next;
9147
+ });
9148
+ if (currentConversationIdRef.current !== targetConversationId) {
9149
+ setCurrentConversationId(targetConversationId);
9150
+ }
9151
+ return;
9152
+ }
9153
+ if (loadingConversationId === targetConversationId) {
9154
+ return;
9155
+ }
9156
+ const existingActive = activeConversationsRef.current.get(targetConversationId);
9157
+ if (existingActive && existingActive.transcriptLoaded && currentConversationIdRef.current === targetConversationId) {
9158
+ return;
9159
+ }
9160
+ const apiConversation = apiConversations.find(
9161
+ (conv) => conv.conversationId === targetConversationId
9162
+ );
9163
+ const targetAgentId = (apiConversation == null ? void 0 : apiConversation.agentId) || (existingActive == null ? void 0 : existingActive.agentId) || currentAgentId;
9164
+ if (!targetAgentId) {
9165
+ return;
9166
+ }
9167
+ if (targetAgentId !== currentAgentId) {
9168
+ setCurrentAgentId(targetAgentId);
6613
9169
  }
6614
- }), [apiKey, currentAgentId, getAgent, onConversationChange]);
9170
+ const targetTitle = conversationFirstPrompts[targetConversationId] || (apiConversation == null ? void 0 : apiConversation.title) || (existingActive == null ? void 0 : existingActive.title);
9171
+ void loadConversationTranscript(
9172
+ targetConversationId,
9173
+ targetAgentId,
9174
+ targetTitle,
9175
+ false
9176
+ );
9177
+ }, [
9178
+ apiConversations,
9179
+ controlledConversationId,
9180
+ conversationFirstPrompts,
9181
+ currentAgentId,
9182
+ isConversationControlled,
9183
+ loadConversationTranscript,
9184
+ loadingConversationId
9185
+ ]);
6615
9186
  const handleRefreshConversations = useCallback4(() => {
6616
9187
  fetchConversations(currentAgentId);
6617
9188
  }, [currentAgentId, fetchConversations]);
@@ -6628,30 +9199,18 @@ var AIAgentPanel = React15.forwardRef(({
6628
9199
  }
6629
9200
  }, [agentsLoading, currentAgentId, apiKey, fetchConversations, getAgent, showConversationHistory]);
6630
9201
  const handleNewConversation = useCallback4(() => {
6631
- const tempId = `new-${Date.now()}`;
6632
- setActiveConversations((prev) => {
6633
- const next = new Map(prev);
6634
- next.set(tempId, {
6635
- conversationId: tempId,
6636
- stableKey: tempId,
6637
- // Stable key never changes even when conversationId updates
6638
- agentId: currentAgentId,
6639
- history: {},
6640
- isLoading: false,
6641
- title: "New conversation"
6642
- });
6643
- return next;
6644
- });
6645
- setCurrentConversationId(tempId);
6646
- }, [currentAgentId]);
9202
+ const tempId = createDraftConversation(currentAgentId);
9203
+ commitConversationSelection(tempId, true);
9204
+ }, [commitConversationSelection, createDraftConversation, currentAgentId]);
6647
9205
  useEffect10(() => {
6648
9206
  var _a2;
9207
+ if (isConversationControlled) return;
6649
9208
  const agentProfile = getAgent(currentAgentId);
6650
9209
  const isAgentReady = (_a2 = agentProfile == null ? void 0 : agentProfile.metadata) == null ? void 0 : _a2.projectId;
6651
9210
  if (isAgentReady && !agentsLoading && activeConversations.size === 0) {
6652
9211
  handleNewConversation();
6653
9212
  }
6654
- }, [currentAgentId, agentsLoading, activeConversations.size, getAgent, handleNewConversation]);
9213
+ }, [currentAgentId, agentsLoading, activeConversations.size, getAgent, handleNewConversation, isConversationControlled]);
6655
9214
  const handleCloseConversation = useCallback4((conversationId, e) => {
6656
9215
  var _a2;
6657
9216
  if (e) {
@@ -6665,9 +9224,13 @@ var AIAgentPanel = React15.forwardRef(({
6665
9224
  if (currentConversationIdRef.current === conversationId) {
6666
9225
  const remaining = Array.from(activeConversationsRef.current.keys()).filter((id) => id !== conversationId);
6667
9226
  const nextId = remaining.length > 0 ? (_a2 = remaining[0]) != null ? _a2 : null : null;
6668
- setCurrentConversationId(nextId);
9227
+ if (nextId) {
9228
+ commitConversationSelection(nextId, true);
9229
+ } else if (!isConversationControlled) {
9230
+ setCurrentConversationId(null);
9231
+ }
6669
9232
  }
6670
- }, []);
9233
+ }, [commitConversationSelection, isConversationControlled]);
6671
9234
  const handleSelectConversation = useCallback4((conversationId) => {
6672
9235
  loadConversationTranscript(conversationId);
6673
9236
  }, [loadConversationTranscript]);
@@ -6968,6 +9531,7 @@ var AIAgentPanel = React15.forwardRef(({
6968
9531
  }
6969
9532
  next.set(targetConversationId, __spreadProps(__spreadValues({}, existing), {
6970
9533
  history,
9534
+ transcriptLoaded: true,
6971
9535
  title
6972
9536
  }));
6973
9537
  return next;
@@ -7057,26 +9621,23 @@ var AIAgentPanel = React15.forwardRef(({
7057
9621
  return prev;
7058
9622
  });
7059
9623
  if (currentConversationIdRef.current === tempId) {
7060
- setCurrentConversationId(realId);
7061
- if (onConversationChange) {
7062
- onConversationChange(realId);
7063
- }
9624
+ commitConversationSelection(realId, true);
7064
9625
  }
7065
- }, [onConversationChange]);
9626
+ }, [commitConversationSelection]);
7066
9627
  const toggleCollapse = useCallback4(() => {
7067
9628
  if (!collapsible) return;
7068
9629
  const newValue = !isCollapsed;
9630
+ if (!newValue) {
9631
+ setIsHistoryCollapsed(true);
9632
+ setShowSearch(false);
9633
+ }
7069
9634
  if (!isControlled) {
7070
9635
  setUncontrolledIsCollapsed(newValue);
7071
9636
  }
7072
9637
  onCollapsedChange == null ? void 0 : onCollapsedChange(newValue);
7073
9638
  }, [collapsible, isCollapsed, isControlled, onCollapsedChange]);
7074
9639
  const toggleHistoryCollapse = useCallback4(() => {
7075
- setIsHistoryCollapsed((prev) => {
7076
- const next = !prev;
7077
- localStorage.setItem("ai-agent-panel-history-collapsed", String(next));
7078
- return next;
7079
- });
9640
+ setIsHistoryCollapsed((prev) => !prev);
7080
9641
  }, []);
7081
9642
  const panelClasses = [
7082
9643
  "ai-agent-panel",
@@ -7148,27 +9709,11 @@ var AIAgentPanel = React15.forwardRef(({
7148
9709
  }
7149
9710
  onCollapsedChange == null ? void 0 : onCollapsedChange(false);
7150
9711
  if (hasActiveConversation && activeConvForAgent) {
7151
- setCurrentConversationId(activeConvForAgent.conversationId);
7152
9712
  setCurrentAgentId(agent.id);
7153
- if (onConversationChange) {
7154
- onConversationChange(activeConvForAgent.conversationId);
7155
- }
9713
+ commitConversationSelection(activeConvForAgent.conversationId, true);
7156
9714
  } else {
7157
- const tempId = `new-${Date.now()}`;
7158
- setActiveConversations((prev) => {
7159
- const next = new Map(prev);
7160
- next.set(tempId, {
7161
- conversationId: tempId,
7162
- stableKey: tempId,
7163
- // Stable key never changes
7164
- agentId: agent.id,
7165
- history: {},
7166
- isLoading: false,
7167
- title: "New conversation"
7168
- });
7169
- return next;
7170
- });
7171
- setCurrentConversationId(tempId);
9715
+ const tempId = createDraftConversation(agent.id);
9716
+ commitConversationSelection(tempId, true);
7172
9717
  setCurrentAgentId(agent.id);
7173
9718
  }
7174
9719
  },
@@ -7253,27 +9798,11 @@ var AIAgentPanel = React15.forwardRef(({
7253
9798
  title: agent.name,
7254
9799
  onClick: () => {
7255
9800
  if (hasActiveConversation && activeConvForAgent) {
7256
- setCurrentConversationId(activeConvForAgent.conversationId);
7257
9801
  setCurrentAgentId(agent.id);
7258
- if (onConversationChange) {
7259
- onConversationChange(activeConvForAgent.conversationId);
7260
- }
9802
+ commitConversationSelection(activeConvForAgent.conversationId, true);
7261
9803
  } else {
7262
- const tempId = `new-${Date.now()}`;
7263
- setActiveConversations((prev) => {
7264
- const next = new Map(prev);
7265
- next.set(tempId, {
7266
- conversationId: tempId,
7267
- stableKey: tempId,
7268
- // Stable key never changes
7269
- agentId: agent.id,
7270
- history: {},
7271
- isLoading: false,
7272
- title: "New conversation"
7273
- });
7274
- return next;
7275
- });
7276
- setCurrentConversationId(tempId);
9804
+ const tempId = createDraftConversation(agent.id);
9805
+ commitConversationSelection(tempId, true);
7277
9806
  setCurrentAgentId(agent.id);
7278
9807
  }
7279
9808
  },
@@ -7326,10 +9855,7 @@ var AIAgentPanel = React15.forwardRef(({
7326
9855
  key: activeConv.stableKey,
7327
9856
  className: `ai-agent-panel__conversation ai-agent-panel__conversation--active-item ${currentConversationId === activeConv.conversationId ? "ai-agent-panel__conversation--current" : ""}`,
7328
9857
  onClick: () => {
7329
- setCurrentConversationId(activeConv.conversationId);
7330
- if (onConversationChange) {
7331
- onConversationChange(activeConv.conversationId);
7332
- }
9858
+ commitConversationSelection(activeConv.conversationId, true);
7333
9859
  }
7334
9860
  },
7335
9861
  /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__conversation-content" }, /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__conversation-title" }, activeConv.isLoading && /* @__PURE__ */ React15.createElement(LoadingDotIcon, null), /* @__PURE__ */ React15.createElement("span", null, activeConv.title))),
@@ -7350,12 +9876,12 @@ var AIAgentPanel = React15.forwardRef(({
7350
9876
  },
7351
9877
  /* @__PURE__ */ React15.createElement("span", null, group.label, " ", group.count > 0 && `(${group.count})`),
7352
9878
  /* @__PURE__ */ React15.createElement("span", { className: "ai-agent-panel__group-chevron" }, expandedSections[group.label] ? "\u25BC" : "\u25B6")
7353
- ), expandedSections[group.label] && group.conversations.length > 0 && group.conversations.map((conv) => {
9879
+ ), expandedSections[group.label] && group.conversations.length > 0 && group.conversations.map((conv, convIndex) => {
7354
9880
  const isActive = activeConversations.has(conv.conversationId);
7355
9881
  return /* @__PURE__ */ React15.createElement(
7356
9882
  "div",
7357
9883
  {
7358
- key: conv.conversationId,
9884
+ key: `${group.label}-${conv.conversationId}-${convIndex}`,
7359
9885
  className: `ai-agent-panel__conversation ${currentConversationId === conv.conversationId ? "ai-agent-panel__conversation--current" : ""} ${isActive ? "ai-agent-panel__conversation--in-active" : ""}`,
7360
9886
  onClick: () => handleConversationSelect(conv)
7361
9887
  },
@@ -7365,7 +9891,7 @@ var AIAgentPanel = React15.forwardRef(({
7365
9891
  /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__chat" }, !showConversationHistory && collapsible && /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__chat-header" }, /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__chat-header-spacer" }), /* @__PURE__ */ React15.createElement(Tooltip, { content: "Collapse Panel", side: position === "right" ? "left" : "right" }, /* @__PURE__ */ React15.createElement(Button, { variant: "ghost", size: "icon", onClick: toggleCollapse }, position === "right" ? /* @__PURE__ */ React15.createElement(ChevronRightIcon, null) : /* @__PURE__ */ React15.createElement(ChevronLeftIcon, null)))), showContextNotification && /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__context-notification" }, /* @__PURE__ */ React15.createElement(SparkleIcon, null), /* @__PURE__ */ React15.createElement("span", null, "Agent now has new context")), activeConversationsList.map((activeConv) => /* @__PURE__ */ React15.createElement(
7366
9892
  ChatPanelWrapper,
7367
9893
  {
7368
- key: `${activeConv.stableKey}-${activeConv.agentId}`,
9894
+ key: activeConv.stableKey,
7369
9895
  activeConv,
7370
9896
  currentConversationId,
7371
9897
  getAgent,
@@ -7396,6 +9922,7 @@ var AIAgentPanel = React15.forwardRef(({
7396
9922
  disabledSectionIds: currentDisabledSections,
7397
9923
  onToggleSection: handleContextSectionToggle,
7398
9924
  onConversationCreated: handleConversationCreated,
9925
+ onBeforeSend,
7399
9926
  conversationInitialPrompt: activeConv.conversationInitialPrompt,
7400
9927
  cssUrl,
7401
9928
  markdownClass,
@@ -7411,7 +9938,11 @@ var AIAgentPanel = React15.forwardRef(({
7411
9938
  callToActionEmailAddress,
7412
9939
  callToActionEmailSubject,
7413
9940
  customerEmailCaptureMode,
7414
- customerEmailCapturePlaceholder
9941
+ customerEmailCapturePlaceholder,
9942
+ resolveMcpAuthHeaders,
9943
+ localToolExecutors,
9944
+ traceContextMode,
9945
+ autoApproveTools
7415
9946
  }
7416
9947
  )), loadingConversationId && /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__conversation-loading-overlay" }, /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__loading-spinner" }), /* @__PURE__ */ React15.createElement("p", null, "Loading conversation...")), currentAgentMetadata && activeConversationsList.length === 0 && !loadingConversationId && /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__empty-chat" }, /* @__PURE__ */ React15.createElement(MessageIcon, null), /* @__PURE__ */ React15.createElement("p", null, "Select a conversation or start a new one"), /* @__PURE__ */ React15.createElement(Button, { variant: "default", size: "sm", onClick: handleNewConversation }, "New Conversation")), agentsLoading && !currentAgentMetadata && /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__loading" }, /* @__PURE__ */ React15.createElement("div", { className: "ai-agent-panel__loading-spinner" }), /* @__PURE__ */ React15.createElement("p", null, "Loading agent..."))),
7417
9948
  /* @__PURE__ */ React15.createElement(