@probelabs/probe 0.6.0-rc159 → 0.6.0-rc162

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.
Files changed (43) hide show
  1. package/README.md +169 -1
  2. package/build/agent/FallbackManager.d.ts +176 -0
  3. package/build/agent/FallbackManager.js +545 -0
  4. package/build/agent/ProbeAgent.d.ts +7 -1
  5. package/build/agent/ProbeAgent.js +545 -89
  6. package/build/agent/RetryManager.d.ts +157 -0
  7. package/build/agent/RetryManager.js +334 -0
  8. package/build/agent/acp/tools.js +6 -2
  9. package/build/agent/contextCompactor.js +271 -0
  10. package/build/agent/index.js +2165 -250
  11. package/build/agent/probeTool.js +20 -2
  12. package/build/agent/schemaUtils.js +7 -0
  13. package/build/agent/tools.js +16 -0
  14. package/build/agent/xmlParsingUtils.js +24 -4
  15. package/build/index.js +13 -0
  16. package/build/tools/common.js +21 -4
  17. package/build/tools/edit.js +409 -0
  18. package/build/tools/index.js +11 -0
  19. package/cjs/agent/ProbeAgent.cjs +2620 -606
  20. package/cjs/index.cjs +2582 -563
  21. package/package.json +2 -2
  22. package/src/agent/FallbackManager.d.ts +176 -0
  23. package/src/agent/FallbackManager.js +545 -0
  24. package/src/agent/ProbeAgent.d.ts +7 -1
  25. package/src/agent/ProbeAgent.js +545 -89
  26. package/src/agent/RetryManager.d.ts +157 -0
  27. package/src/agent/RetryManager.js +334 -0
  28. package/src/agent/acp/tools.js +6 -2
  29. package/src/agent/contextCompactor.js +271 -0
  30. package/src/agent/index.js +30 -1
  31. package/src/agent/probeTool.js +20 -2
  32. package/src/agent/schemaUtils.js +7 -0
  33. package/src/agent/tools.js +16 -0
  34. package/src/agent/xmlParsingUtils.js +24 -4
  35. package/src/index.js +13 -0
  36. package/src/tools/common.js +21 -4
  37. package/src/tools/edit.js +409 -0
  38. package/src/tools/index.js +11 -0
  39. package/bin/binaries/probe-v0.6.0-rc159-aarch64-apple-darwin.tar.gz +0 -0
  40. package/bin/binaries/probe-v0.6.0-rc159-aarch64-unknown-linux-musl.tar.gz +0 -0
  41. package/bin/binaries/probe-v0.6.0-rc159-x86_64-apple-darwin.tar.gz +0 -0
  42. package/bin/binaries/probe-v0.6.0-rc159-x86_64-pc-windows-msvc.zip +0 -0
  43. package/bin/binaries/probe-v0.6.0-rc159-x86_64-unknown-linux-musl.tar.gz +0 -0
@@ -552,6 +552,162 @@ var init_BytePairEncodingCore = __esm({
552
552
  }
553
553
  });
554
554
 
555
+ // node_modules/gpt-tokenizer/esm/functionCalling.js
556
+ function countMessageTokens(message, countStringTokens) {
557
+ let tokens = 0;
558
+ if (message.role) {
559
+ tokens += countStringTokens(message.role);
560
+ }
561
+ if (message.content) {
562
+ tokens += countStringTokens(message.content);
563
+ }
564
+ if (message.name) {
565
+ tokens += countStringTokens(message.name) + MESSAGE_NAME_TOKEN_OVERHEAD;
566
+ }
567
+ if (message.function_call) {
568
+ const { name, arguments: args } = message.function_call;
569
+ if (name) {
570
+ tokens += countStringTokens(name);
571
+ }
572
+ if (args) {
573
+ tokens += countStringTokens(args);
574
+ }
575
+ tokens += FUNCTION_CALL_METADATA_TOKEN_OVERHEAD;
576
+ }
577
+ tokens += MESSAGE_TOKEN_OVERHEAD;
578
+ if (message.role === "function") {
579
+ tokens -= FUNCTION_ROLE_TOKEN_DISCOUNT;
580
+ }
581
+ return tokens;
582
+ }
583
+ function formatObjectProperties(obj, indent, formatType) {
584
+ if (!obj.properties) {
585
+ return "";
586
+ }
587
+ const lines = [];
588
+ const requiredParams = new Set(obj.required ?? []);
589
+ const indentString = " ".repeat(indent);
590
+ for (const [name, param] of Object.entries(obj.properties)) {
591
+ if (param.description && indent < 2) {
592
+ lines.push(`${indentString}// ${param.description}`);
593
+ }
594
+ const isRequired = requiredParams.has(name);
595
+ const formattedType = formatType(param, indent);
596
+ lines.push(`${indentString}${name}${isRequired ? "" : "?"}: ${formattedType},`);
597
+ }
598
+ return lines.join("\n");
599
+ }
600
+ function formatFunctionType(param, indent) {
601
+ switch (param.type) {
602
+ case "string":
603
+ return param.enum?.map((value) => JSON.stringify(value)).join(" | ") ?? "string";
604
+ case "integer":
605
+ case "number":
606
+ return param.enum?.map((value) => `${value}`).join(" | ") ?? "number";
607
+ case "boolean":
608
+ return "boolean";
609
+ case "null":
610
+ return "null";
611
+ case "array":
612
+ return param.items ? `${formatFunctionType(param.items, indent)}[]` : "any[]";
613
+ case "object": {
614
+ const inner = formatObjectProperties(param, indent + 2, formatFunctionType);
615
+ const closingIndent = " ".repeat(indent);
616
+ return `{
617
+ ${inner}
618
+ ${closingIndent}}`;
619
+ }
620
+ default:
621
+ return "any";
622
+ }
623
+ }
624
+ function formatFunctionDefinitions(functions) {
625
+ const lines = ["namespace functions {", ""];
626
+ for (const fn of functions) {
627
+ if (fn.description) {
628
+ lines.push(`// ${fn.description}`);
629
+ }
630
+ const { parameters } = fn;
631
+ const properties = parameters?.properties;
632
+ if (!parameters || !properties || Object.keys(properties).length === 0) {
633
+ lines.push(`type ${fn.name} = () => any;`);
634
+ } else {
635
+ lines.push(`type ${fn.name} = (_: {`);
636
+ const formattedProperties = formatObjectProperties(parameters, 0, formatFunctionType);
637
+ if (formattedProperties.length > 0) {
638
+ lines.push(formattedProperties);
639
+ }
640
+ lines.push("}) => any;");
641
+ }
642
+ lines.push("");
643
+ }
644
+ lines.push("} // namespace functions");
645
+ return lines.join("\n");
646
+ }
647
+ function estimateTokensInFunctions(functions, countStringTokens) {
648
+ const formatted = formatFunctionDefinitions(functions);
649
+ let tokens = countStringTokens(formatted);
650
+ tokens += FUNCTION_DEFINITION_TOKEN_OVERHEAD;
651
+ return tokens;
652
+ }
653
+ function padSystemMessage(message, hasFunctions, isSystemPadded) {
654
+ if (!hasFunctions || isSystemPadded || message.role !== "system") {
655
+ return message;
656
+ }
657
+ if (!message.content || message.content.endsWith(NEWLINE)) {
658
+ return message;
659
+ }
660
+ return {
661
+ ...message,
662
+ content: `${message.content}${NEWLINE}`
663
+ };
664
+ }
665
+ function computeChatCompletionTokenCount(request, countStringTokens) {
666
+ const { messages, functions, function_call: functionCall } = request;
667
+ const hasFunctions = Boolean(functions && functions.length > 0);
668
+ let paddedSystem = false;
669
+ let total = 0;
670
+ for (const message of messages) {
671
+ const messageToCount = padSystemMessage(message, hasFunctions, paddedSystem);
672
+ if (messageToCount !== message && message.role === "system") {
673
+ paddedSystem = true;
674
+ } else if (message.role === "system" && hasFunctions && !paddedSystem) {
675
+ paddedSystem = true;
676
+ }
677
+ total += countMessageTokens(messageToCount, countStringTokens);
678
+ }
679
+ total += COMPLETION_REQUEST_TOKEN_OVERHEAD;
680
+ if (hasFunctions && functions) {
681
+ total += estimateTokensInFunctions(functions, countStringTokens);
682
+ if (messages.some((message) => message.role === "system")) {
683
+ total -= SYSTEM_FUNCTION_TOKEN_DEDUCTION;
684
+ }
685
+ }
686
+ if (functionCall && functionCall !== "auto") {
687
+ if (functionCall === "none") {
688
+ total += FUNCTION_CALL_NONE_TOKEN_OVERHEAD;
689
+ } else if (typeof functionCall === "object" && functionCall.name) {
690
+ total += countStringTokens(functionCall.name) + FUNCTION_CALL_NAME_TOKEN_OVERHEAD;
691
+ }
692
+ }
693
+ return total;
694
+ }
695
+ var MESSAGE_TOKEN_OVERHEAD, MESSAGE_NAME_TOKEN_OVERHEAD, FUNCTION_ROLE_TOKEN_DISCOUNT, FUNCTION_CALL_METADATA_TOKEN_OVERHEAD, FUNCTION_DEFINITION_TOKEN_OVERHEAD, COMPLETION_REQUEST_TOKEN_OVERHEAD, FUNCTION_CALL_NAME_TOKEN_OVERHEAD, FUNCTION_CALL_NONE_TOKEN_OVERHEAD, SYSTEM_FUNCTION_TOKEN_DEDUCTION, NEWLINE;
696
+ var init_functionCalling = __esm({
697
+ "node_modules/gpt-tokenizer/esm/functionCalling.js"() {
698
+ MESSAGE_TOKEN_OVERHEAD = 3;
699
+ MESSAGE_NAME_TOKEN_OVERHEAD = 1;
700
+ FUNCTION_ROLE_TOKEN_DISCOUNT = 2;
701
+ FUNCTION_CALL_METADATA_TOKEN_OVERHEAD = 3;
702
+ FUNCTION_DEFINITION_TOKEN_OVERHEAD = 9;
703
+ COMPLETION_REQUEST_TOKEN_OVERHEAD = 3;
704
+ FUNCTION_CALL_NAME_TOKEN_OVERHEAD = 4;
705
+ FUNCTION_CALL_NONE_TOKEN_OVERHEAD = 1;
706
+ SYSTEM_FUNCTION_TOKEN_DEDUCTION = 4;
707
+ NEWLINE = "\n";
708
+ }
709
+ });
710
+
555
711
  // node_modules/gpt-tokenizer/esm/modelsChatEnabled.gen.js
556
712
  var chatEnabledModels;
557
713
  var init_modelsChatEnabled_gen = __esm({
@@ -565,11 +721,12 @@ var modelsMap_exports = {};
565
721
  __export(modelsMap_exports, {
566
722
  cl100k_base: () => cl100k_base,
567
723
  o200k_base: () => o200k_base,
724
+ o200k_harmony: () => o200k_harmony,
568
725
  p50k_base: () => p50k_base,
569
726
  p50k_edit: () => p50k_edit,
570
727
  r50k_base: () => r50k_base
571
728
  });
572
- var p50k_base, r50k_base, p50k_edit, cl100k_base, o200k_base;
729
+ var p50k_base, r50k_base, p50k_edit, cl100k_base, o200k_base, o200k_harmony;
573
730
  var init_modelsMap = __esm({
574
731
  "node_modules/gpt-tokenizer/esm/modelsMap.js"() {
575
732
  p50k_base = [
@@ -644,11 +801,15 @@ var init_modelsMap = __esm({
644
801
  "davinci-002"
645
802
  ];
646
803
  o200k_base = [];
804
+ o200k_harmony = [
805
+ "gpt-oss-20b",
806
+ "gpt-oss-120b"
807
+ ];
647
808
  }
648
809
  });
649
810
 
650
811
  // node_modules/gpt-tokenizer/esm/specialTokens.js
651
- var EndOfText, FimPrefix, FimMiddle, FimSuffix, ImStart, ImEnd, ImSep, EndOfPrompt;
812
+ var EndOfText, FimPrefix, FimMiddle, FimSuffix, ImStart, ImEnd, ImSep, EndOfPrompt, HarmonyStartOfText, HarmonyStart, HarmonyEnd, HarmonyMessage, HarmonyChannel, HarmonyReturn, HarmonyConstrain, HarmonyCall;
652
813
  var init_specialTokens = __esm({
653
814
  "node_modules/gpt-tokenizer/esm/specialTokens.js"() {
654
815
  EndOfText = "<|endoftext|>";
@@ -659,6 +820,14 @@ var init_specialTokens = __esm({
659
820
  ImEnd = "<|im_end|>";
660
821
  ImSep = "<|im_sep|>";
661
822
  EndOfPrompt = "<|endofprompt|>";
823
+ HarmonyStartOfText = "<|startoftext|>";
824
+ HarmonyStart = "<|start|>";
825
+ HarmonyEnd = "<|end|>";
826
+ HarmonyMessage = "<|message|>";
827
+ HarmonyChannel = "<|channel|>";
828
+ HarmonyReturn = "<|return|>";
829
+ HarmonyConstrain = "<|constrain|>";
830
+ HarmonyCall = "<|call|>";
662
831
  }
663
832
  });
664
833
 
@@ -685,11 +854,16 @@ var init_mapping = __esm({
685
854
  });
686
855
 
687
856
  // node_modules/gpt-tokenizer/esm/encodingParams/constants.js
688
- var R50K_TOKEN_SPLIT_REGEX, CL_AND_O_TOKEN_SPLIT_PATTERN;
857
+ var R50K_TOKEN_SPLIT_REGEX, CONTRACTION_SUFFIX_PATTERN, OPTIONAL_CONTRACTION_SUFFIX, CL100K_TOKEN_SPLIT_PATTERN, CL100K_TOKEN_SPLIT_REGEX, O200K_TOKEN_SPLIT_PATTERN, O200K_TOKEN_SPLIT_REGEX;
689
858
  var init_constants2 = __esm({
690
859
  "node_modules/gpt-tokenizer/esm/encodingParams/constants.js"() {
691
860
  R50K_TOKEN_SPLIT_REGEX = new RegExp("'s|'t|'re|'ve|'m|'ll|'d| ?\\p{L}+| ?\\p{N}+| ?[^\\s\\p{L}\\p{N}]+|\\s+(?!\\S)|\\s+", "gu");
692
- CL_AND_O_TOKEN_SPLIT_PATTERN = new RegExp("(?:'s|'t|'re|'ve|'m|'ll|'d)|[^\\r\\n\\p{L}\\p{N}]?\\p{L}+|\\p{N}{1,3}| ?[^\\s\\p{L}\\p{N}]+[\\r\\n]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+", "giu");
861
+ CONTRACTION_SUFFIX_PATTERN = String.raw`'(?:[sS]|[dD]|[mM]|[tT]|[lL][lL]|[vV][eE]|[rR][eE])`;
862
+ OPTIONAL_CONTRACTION_SUFFIX = String.raw`(?:${CONTRACTION_SUFFIX_PATTERN})?`;
863
+ CL100K_TOKEN_SPLIT_PATTERN = String.raw`${CONTRACTION_SUFFIX_PATTERN}|[^\r\n\p{L}\p{N}]?\p{L}+|\p{N}{1,3}| ?[^\s\p{L}\p{N}]+[\r\n]*|\s+$|\s*[\r\n]|\s+(?!\S)|\s`;
864
+ CL100K_TOKEN_SPLIT_REGEX = new RegExp(CL100K_TOKEN_SPLIT_PATTERN, "gu");
865
+ O200K_TOKEN_SPLIT_PATTERN = String.raw`[^\r\n\p{L}\p{N}]?[\p{Lu}\p{Lt}\p{Lm}\p{Lo}\p{M}]*[\p{Ll}\p{Lm}\p{Lo}\p{M}]+${OPTIONAL_CONTRACTION_SUFFIX}|[^\r\n\p{L}\p{N}]?[\p{Lu}\p{Lt}\p{Lm}\p{Lo}\p{M}]+[\p{Ll}\p{Lm}\p{Lo}\p{M}]*${OPTIONAL_CONTRACTION_SUFFIX}|\p{N}{1,3}| ?[^\s\p{L}\p{N}]+[\r\n/]*|\s*[\r\n]+|\s+(?!\S)|\s+`;
866
+ O200K_TOKEN_SPLIT_REGEX = new RegExp(O200K_TOKEN_SPLIT_PATTERN, "gu");
693
867
  }
694
868
  });
695
869
 
@@ -706,7 +880,7 @@ function Cl100KBase(bytePairRankDecoder) {
706
880
  [EndOfPrompt, 100276]
707
881
  ]);
708
882
  return {
709
- tokenSplitRegex: CL_AND_O_TOKEN_SPLIT_PATTERN,
883
+ tokenSplitRegex: CL100K_TOKEN_SPLIT_REGEX,
710
884
  bytePairRankDecoder,
711
885
  specialTokensEncoder: specialTokenMapping
712
886
  };
@@ -720,26 +894,69 @@ var init_cl100k_base = __esm({
720
894
 
721
895
  // node_modules/gpt-tokenizer/esm/encodingParams/o200k_base.js
722
896
  function O200KBase(bytePairRankDecoder) {
723
- const specialTokenMapping = /* @__PURE__ */ new Map([
724
- [EndOfText, 199999],
725
- [FimPrefix, 2e5],
726
- [FimMiddle, 200001],
727
- [FimSuffix, 200002],
728
- [ImStart, 200003],
729
- [ImEnd, 200004],
730
- [ImSep, 200005],
731
- [EndOfPrompt, 200006]
732
- ]);
733
897
  return {
734
- tokenSplitRegex: CL_AND_O_TOKEN_SPLIT_PATTERN,
898
+ tokenSplitRegex: O200K_TOKEN_SPLIT_REGEX,
735
899
  bytePairRankDecoder,
736
- specialTokensEncoder: specialTokenMapping
900
+ specialTokensEncoder: createO200KSpecialTokenMap()
737
901
  };
738
902
  }
903
+ var O200K_BASE_SPECIAL_TOKEN_ENTRIES, createO200KSpecialTokenMap;
739
904
  var init_o200k_base2 = __esm({
740
905
  "node_modules/gpt-tokenizer/esm/encodingParams/o200k_base.js"() {
741
906
  init_specialTokens();
742
907
  init_constants2();
908
+ O200K_BASE_SPECIAL_TOKEN_ENTRIES = [
909
+ [EndOfText, 199999],
910
+ [FimPrefix, 2e5],
911
+ [FimMiddle, 200001],
912
+ [FimSuffix, 200002],
913
+ [ImStart, 200003],
914
+ [ImEnd, 200004],
915
+ [ImSep, 200005],
916
+ [EndOfPrompt, 200006]
917
+ ];
918
+ createO200KSpecialTokenMap = () => new Map(O200K_BASE_SPECIAL_TOKEN_ENTRIES);
919
+ }
920
+ });
921
+
922
+ // node_modules/gpt-tokenizer/esm/encodingParams/o200k_harmony.js
923
+ function O200KHarmony(bytePairRankDecoder) {
924
+ const specialTokensEncoder = new Map(STATIC_SPECIAL_TOKEN_ENTRIES);
925
+ for (let tokenId = RESERVED_TOKEN_RANGE_START; tokenId < RESERVED_TOKEN_RANGE_END; tokenId += 1) {
926
+ specialTokensEncoder.set(`<|reserved_${tokenId}|>`, tokenId);
927
+ }
928
+ specialTokensEncoder.set(EndOfPrompt, 200018);
929
+ return {
930
+ tokenSplitRegex: O200K_TOKEN_SPLIT_REGEX,
931
+ bytePairRankDecoder,
932
+ specialTokensEncoder,
933
+ chatFormatter: "harmony"
934
+ };
935
+ }
936
+ var RESERVED_TOKEN_RANGE_START, RESERVED_TOKEN_RANGE_END, STATIC_SPECIAL_TOKEN_ENTRIES;
937
+ var init_o200k_harmony = __esm({
938
+ "node_modules/gpt-tokenizer/esm/encodingParams/o200k_harmony.js"() {
939
+ init_specialTokens();
940
+ init_constants2();
941
+ RESERVED_TOKEN_RANGE_START = 200013;
942
+ RESERVED_TOKEN_RANGE_END = 201088;
943
+ STATIC_SPECIAL_TOKEN_ENTRIES = [
944
+ [HarmonyStartOfText, 199998],
945
+ [EndOfText, 199999],
946
+ ["<|reserved_200000|>", 2e5],
947
+ ["<|reserved_200001|>", 200001],
948
+ [HarmonyReturn, 200002],
949
+ [HarmonyConstrain, 200003],
950
+ ["<|reserved_200004|>", 200004],
951
+ [HarmonyChannel, 200005],
952
+ [HarmonyStart, 200006],
953
+ [HarmonyEnd, 200007],
954
+ [HarmonyMessage, 200008],
955
+ ["<|reserved_200009|>", 200009],
956
+ ["<|reserved_200010|>", 200010],
957
+ ["<|reserved_200011|>", 200011],
958
+ [HarmonyCall, 200012]
959
+ ];
743
960
  }
744
961
  });
745
962
 
@@ -813,6 +1030,8 @@ function getEncodingParams(encodingName, getMergeableRanks) {
813
1030
  return Cl100KBase(mergeableBytePairRanks);
814
1031
  case "o200k_base":
815
1032
  return O200KBase(mergeableBytePairRanks);
1033
+ case "o200k_harmony":
1034
+ return O200KHarmony(mergeableBytePairRanks);
816
1035
  default:
817
1036
  throw new Error(`Unknown encoding name: ${encodingName}`);
818
1037
  }
@@ -821,6 +1040,7 @@ var init_modelParams = __esm({
821
1040
  "node_modules/gpt-tokenizer/esm/modelParams.js"() {
822
1041
  init_cl100k_base();
823
1042
  init_o200k_base2();
1043
+ init_o200k_harmony();
824
1044
  init_p50k_base();
825
1045
  init_p50k_edit();
826
1046
  init_r50k_base();
@@ -833,6 +1053,7 @@ var init_GptEncoding = __esm({
833
1053
  "node_modules/gpt-tokenizer/esm/GptEncoding.js"() {
834
1054
  init_BytePairEncodingCore();
835
1055
  init_constants();
1056
+ init_functionCalling();
836
1057
  init_mapping();
837
1058
  init_modelParams();
838
1059
  init_specialTokens();
@@ -851,8 +1072,10 @@ var init_GptEncoding = __esm({
851
1072
  specialTokensSet;
852
1073
  allSpecialTokenRegex;
853
1074
  defaultSpecialTokenConfig;
1075
+ chatFormatter;
1076
+ countChatCompletionTokens;
854
1077
  vocabularySize;
855
- constructor({ bytePairRankDecoder: mergeableBytePairRanks, specialTokensEncoder, expectedVocabularySize, modelName, modelSpec, ...rest }) {
1078
+ constructor({ bytePairRankDecoder: mergeableBytePairRanks, specialTokensEncoder, expectedVocabularySize, modelName, modelSpec, chatFormatter, ...rest }) {
856
1079
  this.specialTokensEncoder = specialTokensEncoder;
857
1080
  this.specialTokensSet = new Set(this.specialTokensEncoder.keys());
858
1081
  this.allSpecialTokenRegex = getSpecialTokenRegex(this.specialTokensSet);
@@ -885,8 +1108,69 @@ var init_GptEncoding = __esm({
885
1108
  this.setMergeCacheSize = this.setMergeCacheSize.bind(this);
886
1109
  this.clearMergeCache = this.clearMergeCache.bind(this);
887
1110
  this.estimateCost = this.estimateCost.bind(this);
1111
+ if (modelSpec?.supported_features?.includes("function_calling")) {
1112
+ this.countChatCompletionTokens = this.countChatCompletionTokensInternal.bind(this);
1113
+ }
888
1114
  this.modelName = modelName;
889
1115
  this.modelSpec = modelSpec;
1116
+ this.chatFormatter = chatFormatter ?? "chatml";
1117
+ }
1118
+ *encodeHarmonyChatGenerator(chat, encodeOptions) {
1119
+ const harmonyStart = this.specialTokensEncoder.get(HarmonyStart);
1120
+ const harmonyMessage = this.specialTokensEncoder.get(HarmonyMessage);
1121
+ const harmonyEnd = this.specialTokensEncoder.get(HarmonyEnd);
1122
+ const harmonyReturn = this.specialTokensEncoder.get(HarmonyReturn);
1123
+ const harmonyCall = this.specialTokensEncoder.get(HarmonyCall);
1124
+ const harmonyChannel = this.specialTokensEncoder.get(HarmonyChannel);
1125
+ const harmonyConstrain = this.specialTokensEncoder.get(HarmonyConstrain);
1126
+ if (harmonyStart === void 0 || harmonyMessage === void 0 || harmonyEnd === void 0 || harmonyReturn === void 0 || harmonyCall === void 0 || harmonyChannel === void 0 || harmonyConstrain === void 0) {
1127
+ throw new Error("Harmony chat format requires dedicated special tokens.");
1128
+ }
1129
+ const encodeHeaderText = (text) => text.length > 0 ? this.encode(text) : [];
1130
+ const resolveTerminatorToken = (terminator) => {
1131
+ switch (terminator) {
1132
+ case "<|return|>":
1133
+ return harmonyReturn;
1134
+ case "<|call|>":
1135
+ return harmonyCall;
1136
+ // eslint-disable-next-line unicorn/no-useless-switch-case
1137
+ case "<|end|>":
1138
+ default:
1139
+ return harmonyEnd;
1140
+ }
1141
+ };
1142
+ for (const message of chat) {
1143
+ if (message.content === void 0) {
1144
+ throw new Error("Content must be defined for all messages.");
1145
+ }
1146
+ const roleOrName = message.name ?? message.role ?? "user";
1147
+ yield [harmonyStart];
1148
+ yield encodeHeaderText(roleOrName);
1149
+ const recipientInRole = message.recipient && (message.recipientPlacement === "role" || !message.channel);
1150
+ const recipientInChannel = message.recipient && !recipientInRole;
1151
+ if (recipientInRole) {
1152
+ yield encodeHeaderText(` to=${message.recipient}`);
1153
+ }
1154
+ if (message.channel) {
1155
+ yield [harmonyChannel];
1156
+ yield encodeHeaderText(message.channel);
1157
+ if (recipientInChannel) {
1158
+ yield encodeHeaderText(` to=${message.recipient}`);
1159
+ }
1160
+ }
1161
+ if (message.constraint) {
1162
+ yield [harmonyConstrain];
1163
+ yield encodeHeaderText(message.constraint);
1164
+ }
1165
+ yield [harmonyMessage];
1166
+ yield* this.encodeGenerator(message.content, encodeOptions);
1167
+ yield [resolveTerminatorToken(message.terminator)];
1168
+ }
1169
+ const assistantPrime = encodeOptions?.primeWithAssistantResponse ?? "assistant";
1170
+ if (assistantPrime.length > 0) {
1171
+ yield [harmonyStart];
1172
+ yield encodeHeaderText(assistantPrime);
1173
+ }
890
1174
  }
891
1175
  static getEncodingApi(encodingName, getMergeableRanks) {
892
1176
  const modelParams = getEncodingParams(encodingName, getMergeableRanks);
@@ -959,9 +1243,16 @@ var init_GptEncoding = __esm({
959
1243
  throw new Error("Model name must be provided either during initialization or passed in to the method.");
960
1244
  }
961
1245
  const params = chatModelParams[model];
1246
+ if (!params) {
1247
+ throw new Error(`Model '${model}' does not support chat.`);
1248
+ }
1249
+ if (this.chatFormatter === "harmony") {
1250
+ yield* this.encodeHarmonyChatGenerator(chat, encodeOptions);
1251
+ return;
1252
+ }
962
1253
  const chatStartToken = this.specialTokensEncoder.get(ImStart);
963
1254
  const chatEndToken = this.specialTokensEncoder.get(ImEnd);
964
- if (!params || chatStartToken === void 0 || chatEndToken === void 0) {
1255
+ if (chatStartToken === void 0 || chatEndToken === void 0) {
965
1256
  throw new Error(`Model '${model}' does not support chat.`);
966
1257
  }
967
1258
  const allowedSpecial = /* @__PURE__ */ new Set([ImSep]);
@@ -984,8 +1275,11 @@ var init_GptEncoding = __esm({
984
1275
  yield [chatEndToken];
985
1276
  yield encodedMessageSeparator;
986
1277
  }
987
- yield [chatStartToken];
988
- yield* this.encodeGenerator("assistant", encodeOptions);
1278
+ const assistantPrime = encodeOptions?.primeWithAssistantResponse ?? "assistant";
1279
+ if (assistantPrime.length > 0) {
1280
+ yield [chatStartToken];
1281
+ yield* this.encodeGenerator(assistantPrime, encodeOptions);
1282
+ }
989
1283
  if (encodedRoleSeparator.length > 0) {
990
1284
  yield encodedRoleSeparator;
991
1285
  }
@@ -1043,6 +1337,15 @@ var init_GptEncoding = __esm({
1043
1337
  }
1044
1338
  return count;
1045
1339
  }
1340
+ countStringTokens(text) {
1341
+ if (!text) {
1342
+ return 0;
1343
+ }
1344
+ return this.bytePairEncodingCoreProcessor.countNative(text);
1345
+ }
1346
+ countChatCompletionTokensInternal(request) {
1347
+ return computeChatCompletionTokenCount(request, (text) => this.countStringTokens(text));
1348
+ }
1046
1349
  setMergeCacheSize(size) {
1047
1350
  this.bytePairEncodingCoreProcessor.setMergeCacheSize(size);
1048
1351
  }
@@ -2007,7 +2310,7 @@ async function waitForFileLock(lockPath, binaryPath) {
2007
2310
  }
2008
2311
  } catch {
2009
2312
  }
2010
- await new Promise((resolve5) => setTimeout(resolve5, LOCK_POLL_INTERVAL_MS));
2313
+ await new Promise((resolve6) => setTimeout(resolve6, LOCK_POLL_INTERVAL_MS));
2011
2314
  }
2012
2315
  if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
2013
2316
  console.log(`Timeout waiting for file lock`);
@@ -2978,7 +3281,7 @@ Command: ${command}`;
2978
3281
  }
2979
3282
  }
2980
3283
  function extractWithStdin(binaryPath, cliArgs, content, options) {
2981
- return new Promise((resolve5, reject2) => {
3284
+ return new Promise((resolve6, reject2) => {
2982
3285
  const childProcess = spawn(binaryPath, ["extract", ...cliArgs], {
2983
3286
  stdio: ["pipe", "pipe", "pipe"]
2984
3287
  });
@@ -3000,7 +3303,7 @@ function extractWithStdin(binaryPath, cliArgs, content, options) {
3000
3303
  }
3001
3304
  try {
3002
3305
  const result = processExtractOutput(stdout, options);
3003
- resolve5(result);
3306
+ resolve6(result);
3004
3307
  } catch (error) {
3005
3308
  reject2(error);
3006
3309
  }
@@ -7483,7 +7786,15 @@ function parseXmlToolCall(xmlString, validTools = DEFAULT_VALID_TOOLS) {
7483
7786
  if (openIndex === -1) {
7484
7787
  continue;
7485
7788
  }
7486
- let closeIndex = xmlString.indexOf(closeTag, openIndex + openTag.length);
7789
+ let closeIndex;
7790
+ if (toolName === "attempt_completion") {
7791
+ closeIndex = xmlString.lastIndexOf(closeTag);
7792
+ if (closeIndex !== -1 && closeIndex <= openIndex + openTag.length) {
7793
+ closeIndex = -1;
7794
+ }
7795
+ } else {
7796
+ closeIndex = xmlString.indexOf(closeTag, openIndex + openTag.length);
7797
+ }
7487
7798
  let hasClosingTag = closeIndex !== -1;
7488
7799
  if (closeIndex === -1) {
7489
7800
  closeIndex = xmlString.length;
@@ -7560,7 +7871,7 @@ function parseTargets(targets) {
7560
7871
  }
7561
7872
  return targets.split(/\s+/).filter((f) => f.length > 0);
7562
7873
  }
7563
- var searchSchema, querySchema, extractSchema, delegateSchema, bashSchema, attemptCompletionSchema, searchToolDefinition, queryToolDefinition, extractToolDefinition, delegateToolDefinition, attemptCompletionToolDefinition, searchDescription, queryDescription, extractDescription, delegateDescription, DEFAULT_VALID_TOOLS;
7874
+ var searchSchema, querySchema, extractSchema, delegateSchema, bashSchema, attemptCompletionSchema, searchToolDefinition, queryToolDefinition, extractToolDefinition, delegateToolDefinition, attemptCompletionToolDefinition, bashToolDefinition, searchDescription, queryDescription, extractDescription, delegateDescription, DEFAULT_VALID_TOOLS;
7564
7875
  var init_common = __esm({
7565
7876
  "src/tools/common.js"() {
7566
7877
  "use strict";
@@ -7573,11 +7884,12 @@ var init_common = __esm({
7573
7884
  pattern: external_exports.string().describe("AST pattern to search for. Use $NAME for variable names, $$$PARAMS for parameter lists, etc."),
7574
7885
  path: external_exports.string().optional().default(".").describe("Path to search in"),
7575
7886
  language: external_exports.string().optional().default("rust").describe("Programming language to use for parsing"),
7576
- allow_tests: external_exports.boolean().optional().default(false).describe("Allow test files in search results")
7887
+ allow_tests: external_exports.boolean().optional().default(true).describe("Allow test files in search results")
7577
7888
  });
7578
7889
  extractSchema = external_exports.object({
7579
7890
  targets: external_exports.string().optional().describe('File paths or symbols to extract from. Formats: "file.js" (whole file), "file.js:42" (line 42), "file.js:10-20" (lines 10-20), "file.js#funcName" (symbol). Multiple targets separated by spaces.'),
7580
- input_content: external_exports.string().optional().describe("Text content to extract file paths from (alternative to targets)")
7891
+ input_content: external_exports.string().optional().describe("Text content to extract file paths from (alternative to targets)"),
7892
+ allow_tests: external_exports.boolean().optional().default(true).describe("Include test files in extraction results")
7581
7893
  });
7582
7894
  delegateSchema = external_exports.object({
7583
7895
  task: external_exports.string().describe("The task to delegate to a subagent. Be specific about what needs to be accomplished.")
@@ -7704,7 +8016,7 @@ Parameters:
7704
8016
  - pattern: (required) AST pattern to search for. Use $NAME for variable names, $$$PARAMS for parameter lists, etc.
7705
8017
  - path: (optional, default: '.') Path to search in.
7706
8018
  - language: (optional, default: 'rust') Programming language to use for parsing.
7707
- - allow_tests: (optional, default: false) Allow test files in search results (true/false).
8019
+ - allow_tests: (optional, default: true) Allow test files in search results (true/false).
7708
8020
  Usage Example:
7709
8021
 
7710
8022
  <examples>
@@ -7729,6 +8041,7 @@ Full file extraction should be the LAST RESORT! Always prefer search.
7729
8041
  Parameters:
7730
8042
  - targets: (required) File paths or symbols to extract from. Formats: "file.js" (whole file), "file.js:42" (code block at line 42), "file.js:10-20" (lines 10-20), "file.js#funcName" (specific symbol). Multiple targets separated by spaces.
7731
8043
  - input_content: (optional) Text content to extract file paths from (alternative to targets for processing diffs/logs).
8044
+ - allow_tests: (optional, default: true) Include test files in extraction results.
7732
8045
 
7733
8046
  Usage Example:
7734
8047
 
@@ -7795,6 +8108,61 @@ Usage Example:
7795
8108
  <attempt_completion>
7796
8109
  I have refactored the search module according to the requirements and verified the tests pass. The module now uses the new BM25 ranking algorithm and has improved error handling.
7797
8110
  </attempt_completion>
8111
+ `;
8112
+ bashToolDefinition = `
8113
+ ## bash
8114
+ Description: Execute bash commands for system exploration and development tasks. This tool has built-in security with allow/deny lists. By default, only safe read-only commands are allowed for code exploration.
8115
+
8116
+ Parameters:
8117
+ - command: (required) The bash command to execute
8118
+ - workingDirectory: (optional) Directory to execute the command in
8119
+ - timeout: (optional) Command timeout in milliseconds
8120
+ - env: (optional) Additional environment variables as an object
8121
+
8122
+ Security: Commands are filtered through allow/deny lists for safety:
8123
+ - Allowed by default: ls, cat, git status, npm list, find, grep, etc.
8124
+ - Denied by default: rm -rf, sudo, npm install, dangerous system commands
8125
+
8126
+ Usage Examples:
8127
+
8128
+ <examples>
8129
+
8130
+ User: What files are in the src directory?
8131
+ <bash>
8132
+ <command>ls -la src/</command>
8133
+ </bash>
8134
+
8135
+ User: Show me the git status
8136
+ <bash>
8137
+ <command>git status</command>
8138
+ </bash>
8139
+
8140
+ User: Find all TypeScript files
8141
+ <bash>
8142
+ <command>find . -name "*.ts" -type f</command>
8143
+ </bash>
8144
+
8145
+ User: Check installed npm packages
8146
+ <bash>
8147
+ <command>npm list --depth=0</command>
8148
+ </bash>
8149
+
8150
+ User: Search for TODO comments in code
8151
+ <bash>
8152
+ <command>grep -r "TODO" src/</command>
8153
+ </bash>
8154
+
8155
+ User: Show recent git commits
8156
+ <bash>
8157
+ <command>git log --oneline -10</command>
8158
+ </bash>
8159
+
8160
+ User: Check system info
8161
+ <bash>
8162
+ <command>uname -a</command>
8163
+ </bash>
8164
+
8165
+ </examples>
7798
8166
  `;
7799
8167
  searchDescription = "Search code in the repository using Elasticsearch-like query syntax. Use this tool first for any code-related questions.";
7800
8168
  queryDescription = "Search code using ast-grep structural pattern matching. Use this tool to find specific code structures like functions, classes, or methods.";
@@ -8894,14 +9262,14 @@ async function executeBashCommand(command, options = {}) {
8894
9262
  console.log(`[BashExecutor] Working directory: "${cwd}"`);
8895
9263
  console.log(`[BashExecutor] Timeout: ${timeout}ms`);
8896
9264
  }
8897
- return new Promise((resolve5, reject2) => {
9265
+ return new Promise((resolve6, reject2) => {
8898
9266
  const processEnv = {
8899
9267
  ...process.env,
8900
9268
  ...env
8901
9269
  };
8902
9270
  const args = parseCommandForExecution(command);
8903
9271
  if (!args || args.length === 0) {
8904
- resolve5({
9272
+ resolve6({
8905
9273
  success: false,
8906
9274
  error: "Failed to parse command",
8907
9275
  stdout: "",
@@ -8984,7 +9352,7 @@ async function executeBashCommand(command, options = {}) {
8984
9352
  success = false;
8985
9353
  error = `Command exited with code ${code}`;
8986
9354
  }
8987
- resolve5({
9355
+ resolve6({
8988
9356
  success,
8989
9357
  error,
8990
9358
  stdout: stdout.trim(),
@@ -9004,7 +9372,7 @@ async function executeBashCommand(command, options = {}) {
9004
9372
  if (debug) {
9005
9373
  console.log(`[BashExecutor] Spawn error:`, error);
9006
9374
  }
9007
- resolve5({
9375
+ resolve6({
9008
9376
  success: false,
9009
9377
  error: `Failed to execute command: ${error.message}`,
9010
9378
  stdout: "",
@@ -9276,6 +9644,283 @@ Command failed with exit code ${result.exitCode}`;
9276
9644
  }
9277
9645
  });
9278
9646
 
9647
+ // src/tools/edit.js
9648
+ import { tool as tool3 } from "ai";
9649
+ import { promises as fs4 } from "fs";
9650
+ import { dirname, resolve as resolve3, isAbsolute, sep } from "path";
9651
+ import { existsSync as existsSync2 } from "fs";
9652
+ function isPathAllowed(filePath, allowedFolders) {
9653
+ if (!allowedFolders || allowedFolders.length === 0) {
9654
+ const resolvedPath2 = resolve3(filePath);
9655
+ const cwd = resolve3(process.cwd());
9656
+ return resolvedPath2 === cwd || resolvedPath2.startsWith(cwd + sep);
9657
+ }
9658
+ const resolvedPath = resolve3(filePath);
9659
+ return allowedFolders.some((folder) => {
9660
+ const allowedPath = resolve3(folder);
9661
+ return resolvedPath === allowedPath || resolvedPath.startsWith(allowedPath + sep);
9662
+ });
9663
+ }
9664
+ function parseFileToolOptions(options = {}) {
9665
+ return {
9666
+ debug: options.debug || false,
9667
+ allowedFolders: options.allowedFolders || [],
9668
+ defaultPath: options.defaultPath
9669
+ };
9670
+ }
9671
+ var editTool, createTool, editDescription, createDescription, editToolDefinition, createToolDefinition;
9672
+ var init_edit = __esm({
9673
+ "src/tools/edit.js"() {
9674
+ "use strict";
9675
+ editTool = (options = {}) => {
9676
+ const { debug, allowedFolders, defaultPath } = parseFileToolOptions(options);
9677
+ return tool3({
9678
+ name: "edit",
9679
+ description: `Edit files using exact string replacement (Claude Code style).
9680
+
9681
+ This tool performs exact string replacements in files. It requires the old_string to match exactly what's in the file, including all whitespace and indentation.
9682
+
9683
+ Parameters:
9684
+ - file_path: Path to the file to edit (absolute or relative)
9685
+ - old_string: Exact text to find and replace (must be unique in the file unless replace_all is true)
9686
+ - new_string: Text to replace with
9687
+ - replace_all: (optional) Replace all occurrences instead of requiring uniqueness
9688
+
9689
+ Important:
9690
+ - The old_string must match EXACTLY including whitespace
9691
+ - If old_string appears multiple times and replace_all is false, the edit will fail
9692
+ - Use larger context around the string to ensure uniqueness when needed`,
9693
+ inputSchema: {
9694
+ type: "object",
9695
+ properties: {
9696
+ file_path: {
9697
+ type: "string",
9698
+ description: "Path to the file to edit"
9699
+ },
9700
+ old_string: {
9701
+ type: "string",
9702
+ description: "Exact text to find and replace"
9703
+ },
9704
+ new_string: {
9705
+ type: "string",
9706
+ description: "Text to replace with"
9707
+ },
9708
+ replace_all: {
9709
+ type: "boolean",
9710
+ description: "Replace all occurrences (default: false)",
9711
+ default: false
9712
+ }
9713
+ },
9714
+ required: ["file_path", "old_string", "new_string"]
9715
+ },
9716
+ execute: async ({ file_path, old_string, new_string, replace_all = false }) => {
9717
+ try {
9718
+ if (!file_path || typeof file_path !== "string" || file_path.trim() === "") {
9719
+ return `Error editing file: Invalid file_path - must be a non-empty string`;
9720
+ }
9721
+ if (old_string === void 0 || old_string === null || typeof old_string !== "string") {
9722
+ return `Error editing file: Invalid old_string - must be a string`;
9723
+ }
9724
+ if (new_string === void 0 || new_string === null || typeof new_string !== "string") {
9725
+ return `Error editing file: Invalid new_string - must be a string`;
9726
+ }
9727
+ const resolvedPath = isAbsolute(file_path) ? file_path : resolve3(defaultPath || process.cwd(), file_path);
9728
+ if (debug) {
9729
+ console.error(`[Edit] Attempting to edit file: ${resolvedPath}`);
9730
+ }
9731
+ if (!isPathAllowed(resolvedPath, allowedFolders)) {
9732
+ return `Error editing file: Permission denied - ${file_path} is outside allowed directories`;
9733
+ }
9734
+ if (!existsSync2(resolvedPath)) {
9735
+ return `Error editing file: File not found - ${file_path}`;
9736
+ }
9737
+ const content = await fs4.readFile(resolvedPath, "utf-8");
9738
+ if (!content.includes(old_string)) {
9739
+ return `Error editing file: String not found - the specified old_string was not found in ${file_path}`;
9740
+ }
9741
+ const occurrences = content.split(old_string).length - 1;
9742
+ if (!replace_all && occurrences > 1) {
9743
+ return `Error editing file: Multiple occurrences found - the old_string appears ${occurrences} times. Use replace_all: true to replace all occurrences, or provide more context to make the string unique.`;
9744
+ }
9745
+ let newContent;
9746
+ if (replace_all) {
9747
+ newContent = content.replaceAll(old_string, new_string);
9748
+ } else {
9749
+ newContent = content.replace(old_string, new_string);
9750
+ }
9751
+ if (newContent === content) {
9752
+ return `Error editing file: No changes made - old_string and new_string might be the same`;
9753
+ }
9754
+ await fs4.writeFile(resolvedPath, newContent, "utf-8");
9755
+ const replacedCount = replace_all ? occurrences : 1;
9756
+ if (debug) {
9757
+ console.error(`[Edit] Successfully edited ${resolvedPath}, replaced ${replacedCount} occurrence(s)`);
9758
+ }
9759
+ return `Successfully edited ${file_path} (${replacedCount} replacement${replacedCount !== 1 ? "s" : ""})`;
9760
+ } catch (error) {
9761
+ console.error("[Edit] Error:", error);
9762
+ return `Error editing file: ${error.message}`;
9763
+ }
9764
+ }
9765
+ });
9766
+ };
9767
+ createTool = (options = {}) => {
9768
+ const { debug, allowedFolders, defaultPath } = parseFileToolOptions(options);
9769
+ return tool3({
9770
+ name: "create",
9771
+ description: `Create new files with specified content.
9772
+
9773
+ This tool creates new files in the filesystem. It will create parent directories if they don't exist.
9774
+
9775
+ Parameters:
9776
+ - file_path: Path where the file should be created (absolute or relative)
9777
+ - content: Content to write to the file
9778
+ - overwrite: (optional) Whether to overwrite if file exists (default: false)
9779
+
9780
+ Important:
9781
+ - By default, will fail if the file already exists
9782
+ - Set overwrite: true to replace existing files
9783
+ - Parent directories will be created automatically if needed`,
9784
+ inputSchema: {
9785
+ type: "object",
9786
+ properties: {
9787
+ file_path: {
9788
+ type: "string",
9789
+ description: "Path where the file should be created"
9790
+ },
9791
+ content: {
9792
+ type: "string",
9793
+ description: "Content to write to the file"
9794
+ },
9795
+ overwrite: {
9796
+ type: "boolean",
9797
+ description: "Overwrite if file exists (default: false)",
9798
+ default: false
9799
+ }
9800
+ },
9801
+ required: ["file_path", "content"]
9802
+ },
9803
+ execute: async ({ file_path, content, overwrite = false }) => {
9804
+ try {
9805
+ if (!file_path || typeof file_path !== "string" || file_path.trim() === "") {
9806
+ return `Error creating file: Invalid file_path - must be a non-empty string`;
9807
+ }
9808
+ if (content === void 0 || content === null || typeof content !== "string") {
9809
+ return `Error creating file: Invalid content - must be a string`;
9810
+ }
9811
+ const resolvedPath = isAbsolute(file_path) ? file_path : resolve3(defaultPath || process.cwd(), file_path);
9812
+ if (debug) {
9813
+ console.error(`[Create] Attempting to create file: ${resolvedPath}`);
9814
+ }
9815
+ if (!isPathAllowed(resolvedPath, allowedFolders)) {
9816
+ return `Error creating file: Permission denied - ${file_path} is outside allowed directories`;
9817
+ }
9818
+ if (existsSync2(resolvedPath) && !overwrite) {
9819
+ return `Error creating file: File already exists - ${file_path}. Use overwrite: true to replace it.`;
9820
+ }
9821
+ const dir = dirname(resolvedPath);
9822
+ await fs4.mkdir(dir, { recursive: true });
9823
+ await fs4.writeFile(resolvedPath, content, "utf-8");
9824
+ const action = existsSync2(resolvedPath) && overwrite ? "overwrote" : "created";
9825
+ const bytes = Buffer.byteLength(content, "utf-8");
9826
+ if (debug) {
9827
+ console.error(`[Create] Successfully ${action} ${resolvedPath}`);
9828
+ }
9829
+ return `Successfully ${action} ${file_path} (${bytes} bytes)`;
9830
+ } catch (error) {
9831
+ console.error("[Create] Error:", error);
9832
+ return `Error creating file: ${error.message}`;
9833
+ }
9834
+ }
9835
+ });
9836
+ };
9837
+ editDescription = "Edit files using exact string replacement. Requires exact match including whitespace.";
9838
+ createDescription = "Create new files with specified content. Will create parent directories if needed.";
9839
+ editToolDefinition = `
9840
+ ## edit
9841
+ Description: ${editDescription}
9842
+
9843
+ When to use:
9844
+ - For precise, surgical edits to existing files
9845
+ - When you need to change specific lines or blocks of code
9846
+ - For renaming functions, variables, or updating configuration values
9847
+ - When the exact text to replace is known and unique (or use replace_all for multiple occurrences)
9848
+
9849
+ When NOT to use:
9850
+ - For creating new files (use 'create' tool instead)
9851
+ - When you cannot determine the exact text to replace
9852
+ - When changes span multiple locations that would be better handled together
9853
+
9854
+ Parameters:
9855
+ - file_path: (required) Path to the file to edit
9856
+ - old_string: (required) Exact text to find and replace (must match including whitespace, newlines, and indentation)
9857
+ - new_string: (required) Text to replace with
9858
+ - replace_all: (optional, default: false) Replace all occurrences if the string appears multiple times
9859
+
9860
+ Important notes:
9861
+ - The old_string MUST match EXACTLY, including all whitespace, indentation, and line breaks
9862
+ - If old_string appears multiple times and replace_all is false, the tool will fail
9863
+ - Always verify the exact formatting of the text you want to replace
9864
+
9865
+ Examples:
9866
+ <edit>
9867
+ <file_path>src/main.js</file_path>
9868
+ <old_string>function oldName() {
9869
+ return 42;
9870
+ }</old_string>
9871
+ <new_string>function newName() {
9872
+ return 42;
9873
+ }</new_string>
9874
+ </edit>
9875
+
9876
+ <edit>
9877
+ <file_path>config.json</file_path>
9878
+ <old_string>"debug": false</old_string>
9879
+ <new_string>"debug": true</new_string>
9880
+ <replace_all>true</replace_all>
9881
+ </edit>`;
9882
+ createToolDefinition = `
9883
+ ## create
9884
+ Description: ${createDescription}
9885
+
9886
+ When to use:
9887
+ - For creating brand new files from scratch
9888
+ - When you need to add configuration files, documentation, or new modules
9889
+ - For generating boilerplate code or templates
9890
+ - When you have the complete content ready to write
9891
+
9892
+ When NOT to use:
9893
+ - For editing existing files (use 'edit' tool instead)
9894
+ - When a file already exists unless you explicitly want to overwrite it
9895
+
9896
+ Parameters:
9897
+ - file_path: (required) Path where the file should be created
9898
+ - content: (required) Complete content to write to the file
9899
+ - overwrite: (optional, default: false) Whether to overwrite if file already exists
9900
+
9901
+ Important notes:
9902
+ - Parent directories will be created automatically if they don't exist
9903
+ - The tool will fail if the file already exists and overwrite is false
9904
+ - Be careful with the overwrite option as it completely replaces existing files
9905
+
9906
+ Examples:
9907
+ <create>
9908
+ <file_path>src/newFile.js</file_path>
9909
+ <content>export function hello() {
9910
+ return "Hello, world!";
9911
+ }</content>
9912
+ </create>
9913
+
9914
+ <create>
9915
+ <file_path>README.md</file_path>
9916
+ <content># My Project
9917
+
9918
+ This is a new project.</content>
9919
+ <overwrite>true</overwrite>
9920
+ </create>`;
9921
+ }
9922
+ });
9923
+
9279
9924
  // src/tools/langchain.js
9280
9925
  var init_langchain = __esm({
9281
9926
  "src/tools/langchain.js"() {
@@ -9425,8 +10070,10 @@ var init_tools = __esm({
9425
10070
  "use strict";
9426
10071
  init_vercel();
9427
10072
  init_bash();
10073
+ init_edit();
9428
10074
  init_langchain();
9429
10075
  init_common();
10076
+ init_edit();
9430
10077
  init_system_message();
9431
10078
  init_vercel();
9432
10079
  init_bash();
@@ -9443,7 +10090,7 @@ var init_tools = __esm({
9443
10090
  });
9444
10091
 
9445
10092
  // src/utils/file-lister.js
9446
- import fs4 from "fs";
10093
+ import fs5 from "fs";
9447
10094
  import path4 from "path";
9448
10095
  import { promisify as promisify6 } from "util";
9449
10096
  import { exec as exec4 } from "child_process";
@@ -9453,10 +10100,10 @@ async function listFilesByLevel(options) {
9453
10100
  maxFiles = 100,
9454
10101
  respectGitignore = true
9455
10102
  } = options;
9456
- if (!fs4.existsSync(directory)) {
10103
+ if (!fs5.existsSync(directory)) {
9457
10104
  throw new Error(`Directory does not exist: ${directory}`);
9458
10105
  }
9459
- const gitDirExists = fs4.existsSync(path4.join(directory, ".git"));
10106
+ const gitDirExists = fs5.existsSync(path4.join(directory, ".git"));
9460
10107
  if (gitDirExists && respectGitignore) {
9461
10108
  try {
9462
10109
  return await listFilesUsingGit(directory, maxFiles);
@@ -9487,7 +10134,7 @@ async function listFilesByLevelManually(directory, maxFiles, respectGitignore) {
9487
10134
  while (queue.length > 0 && result.length < maxFiles) {
9488
10135
  const { dir, level } = queue.shift();
9489
10136
  try {
9490
- const entries = fs4.readdirSync(dir, { withFileTypes: true });
10137
+ const entries = fs5.readdirSync(dir, { withFileTypes: true });
9491
10138
  const files = entries.filter((entry) => entry.isFile());
9492
10139
  for (const file of files) {
9493
10140
  if (result.length >= maxFiles) break;
@@ -9512,11 +10159,11 @@ async function listFilesByLevelManually(directory, maxFiles, respectGitignore) {
9512
10159
  }
9513
10160
  function loadGitignorePatterns(directory) {
9514
10161
  const gitignorePath = path4.join(directory, ".gitignore");
9515
- if (!fs4.existsSync(gitignorePath)) {
10162
+ if (!fs5.existsSync(gitignorePath)) {
9516
10163
  return [];
9517
10164
  }
9518
10165
  try {
9519
- const content = fs4.readFileSync(gitignorePath, "utf8");
10166
+ const content = fs5.readFileSync(gitignorePath, "utf8");
9520
10167
  return content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
9521
10168
  } catch (error) {
9522
10169
  console.error(`Warning: Could not read .gitignore: ${error.message}`);
@@ -9543,8 +10190,8 @@ var init_file_lister = __esm({
9543
10190
  });
9544
10191
 
9545
10192
  // src/agent/simpleTelemetry.js
9546
- import { existsSync as existsSync2, mkdirSync, createWriteStream } from "fs";
9547
- import { dirname } from "path";
10193
+ import { existsSync as existsSync3, mkdirSync, createWriteStream } from "fs";
10194
+ import { dirname as dirname2 } from "path";
9548
10195
  function initializeSimpleTelemetryFromOptions(options) {
9549
10196
  const telemetry = new SimpleTelemetry({
9550
10197
  serviceName: "probe-agent",
@@ -9571,8 +10218,8 @@ var init_simpleTelemetry = __esm({
9571
10218
  }
9572
10219
  initializeFileExporter() {
9573
10220
  try {
9574
- const dir = dirname(this.filePath);
9575
- if (!existsSync2(dir)) {
10221
+ const dir = dirname2(this.filePath);
10222
+ if (!existsSync3(dir)) {
9576
10223
  mkdirSync(dir, { recursive: true });
9577
10224
  }
9578
10225
  this.stream = createWriteStream(this.filePath, { flags: "a" });
@@ -9636,20 +10283,20 @@ var init_simpleTelemetry = __esm({
9636
10283
  }
9637
10284
  async flush() {
9638
10285
  if (this.stream) {
9639
- return new Promise((resolve5) => {
9640
- this.stream.once("drain", resolve5);
10286
+ return new Promise((resolve6) => {
10287
+ this.stream.once("drain", resolve6);
9641
10288
  if (!this.stream.writableNeedDrain) {
9642
- resolve5();
10289
+ resolve6();
9643
10290
  }
9644
10291
  });
9645
10292
  }
9646
10293
  }
9647
10294
  async shutdown() {
9648
10295
  if (this.stream) {
9649
- return new Promise((resolve5) => {
10296
+ return new Promise((resolve6) => {
9650
10297
  this.stream.end(() => {
9651
10298
  console.log(`[SimpleTelemetry] File stream closed: ${this.filePath}`);
9652
- resolve5();
10299
+ resolve6();
9653
10300
  });
9654
10301
  });
9655
10302
  }
@@ -10612,7 +11259,7 @@ var init_escape = __esm({
10612
11259
  });
10613
11260
 
10614
11261
  // node_modules/minimatch/dist/esm/index.js
10615
- var import_brace_expansion, minimatch, starDotExtRE, starDotExtTest, starDotExtTestDot, starDotExtTestNocase, starDotExtTestNocaseDot, starDotStarRE, starDotStarTest, starDotStarTestDot, dotStarRE, dotStarTest, starRE, starTest, starTestDot, qmarksRE, qmarksTestNocase, qmarksTestNocaseDot, qmarksTestDot, qmarksTest, qmarksTestNoExt, qmarksTestNoExtDot, defaultPlatform, path5, sep, GLOBSTAR, qmark2, star2, twoStarDot, twoStarNoDot, filter, ext, defaults, braceExpand, makeRe, match, globMagic, regExpEscape2, Minimatch;
11262
+ var import_brace_expansion, minimatch, starDotExtRE, starDotExtTest, starDotExtTestDot, starDotExtTestNocase, starDotExtTestNocaseDot, starDotStarRE, starDotStarTest, starDotStarTestDot, dotStarRE, dotStarTest, starRE, starTest, starTestDot, qmarksRE, qmarksTestNocase, qmarksTestNocaseDot, qmarksTestDot, qmarksTest, qmarksTestNoExt, qmarksTestNoExtDot, defaultPlatform, path5, sep2, GLOBSTAR, qmark2, star2, twoStarDot, twoStarNoDot, filter, ext, defaults, braceExpand, makeRe, match, globMagic, regExpEscape2, Minimatch;
10616
11263
  var init_esm = __esm({
10617
11264
  "node_modules/minimatch/dist/esm/index.js"() {
10618
11265
  import_brace_expansion = __toESM(require_brace_expansion(), 1);
@@ -10685,8 +11332,8 @@ var init_esm = __esm({
10685
11332
  win32: { sep: "\\" },
10686
11333
  posix: { sep: "/" }
10687
11334
  };
10688
- sep = defaultPlatform === "win32" ? path5.win32.sep : path5.posix.sep;
10689
- minimatch.sep = sep;
11335
+ sep2 = defaultPlatform === "win32" ? path5.win32.sep : path5.posix.sep;
11336
+ minimatch.sep = sep2;
10690
11337
  GLOBSTAR = Symbol("globstar **");
10691
11338
  minimatch.GLOBSTAR = GLOBSTAR;
10692
11339
  qmark2 = "[^/]";
@@ -13448,10 +14095,10 @@ var init_esm3 = __esm({
13448
14095
  * Return a void Promise that resolves once the stream ends.
13449
14096
  */
13450
14097
  async promise() {
13451
- return new Promise((resolve5, reject2) => {
14098
+ return new Promise((resolve6, reject2) => {
13452
14099
  this.on(DESTROYED, () => reject2(new Error("stream destroyed")));
13453
14100
  this.on("error", (er) => reject2(er));
13454
- this.on("end", () => resolve5());
14101
+ this.on("end", () => resolve6());
13455
14102
  });
13456
14103
  }
13457
14104
  /**
@@ -13475,7 +14122,7 @@ var init_esm3 = __esm({
13475
14122
  return Promise.resolve({ done: false, value: res });
13476
14123
  if (this[EOF])
13477
14124
  return stop();
13478
- let resolve5;
14125
+ let resolve6;
13479
14126
  let reject2;
13480
14127
  const onerr = (er) => {
13481
14128
  this.off("data", ondata);
@@ -13489,19 +14136,19 @@ var init_esm3 = __esm({
13489
14136
  this.off("end", onend);
13490
14137
  this.off(DESTROYED, ondestroy);
13491
14138
  this.pause();
13492
- resolve5({ value, done: !!this[EOF] });
14139
+ resolve6({ value, done: !!this[EOF] });
13493
14140
  };
13494
14141
  const onend = () => {
13495
14142
  this.off("error", onerr);
13496
14143
  this.off("data", ondata);
13497
14144
  this.off(DESTROYED, ondestroy);
13498
14145
  stop();
13499
- resolve5({ done: true, value: void 0 });
14146
+ resolve6({ done: true, value: void 0 });
13500
14147
  };
13501
14148
  const ondestroy = () => onerr(new Error("stream destroyed"));
13502
14149
  return new Promise((res2, rej) => {
13503
14150
  reject2 = rej;
13504
- resolve5 = res2;
14151
+ resolve6 = res2;
13505
14152
  this.once(DESTROYED, ondestroy);
13506
14153
  this.once("error", onerr);
13507
14154
  this.once("end", onend);
@@ -14481,9 +15128,9 @@ var init_esm4 = __esm({
14481
15128
  if (this.#asyncReaddirInFlight) {
14482
15129
  await this.#asyncReaddirInFlight;
14483
15130
  } else {
14484
- let resolve5 = () => {
15131
+ let resolve6 = () => {
14485
15132
  };
14486
- this.#asyncReaddirInFlight = new Promise((res) => resolve5 = res);
15133
+ this.#asyncReaddirInFlight = new Promise((res) => resolve6 = res);
14487
15134
  try {
14488
15135
  for (const e of await this.#fs.promises.readdir(fullpath, {
14489
15136
  withFileTypes: true
@@ -14496,7 +15143,7 @@ var init_esm4 = __esm({
14496
15143
  children.provisional = 0;
14497
15144
  }
14498
15145
  this.#asyncReaddirInFlight = void 0;
14499
- resolve5();
15146
+ resolve6();
14500
15147
  }
14501
15148
  return children.slice(0, children.provisional);
14502
15149
  }
@@ -14726,8 +15373,8 @@ var init_esm4 = __esm({
14726
15373
  *
14727
15374
  * @internal
14728
15375
  */
14729
- constructor(cwd = process.cwd(), pathImpl, sep2, { nocase, childrenCacheSize = 16 * 1024, fs: fs6 = defaultFS } = {}) {
14730
- this.#fs = fsFromOption(fs6);
15376
+ constructor(cwd = process.cwd(), pathImpl, sep3, { nocase, childrenCacheSize = 16 * 1024, fs: fs7 = defaultFS } = {}) {
15377
+ this.#fs = fsFromOption(fs7);
14731
15378
  if (cwd instanceof URL || cwd.startsWith("file://")) {
14732
15379
  cwd = fileURLToPath4(cwd);
14733
15380
  }
@@ -14737,7 +15384,7 @@ var init_esm4 = __esm({
14737
15384
  this.#resolveCache = new ResolveCache();
14738
15385
  this.#resolvePosixCache = new ResolveCache();
14739
15386
  this.#children = new ChildrenCache(childrenCacheSize);
14740
- const split = cwdPath.substring(this.rootPath.length).split(sep2);
15387
+ const split = cwdPath.substring(this.rootPath.length).split(sep3);
14741
15388
  if (split.length === 1 && !split[0]) {
14742
15389
  split.pop();
14743
15390
  }
@@ -15285,8 +15932,8 @@ var init_esm4 = __esm({
15285
15932
  /**
15286
15933
  * @internal
15287
15934
  */
15288
- newRoot(fs6) {
15289
- return new PathWin32(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs6 });
15935
+ newRoot(fs7) {
15936
+ return new PathWin32(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs7 });
15290
15937
  }
15291
15938
  /**
15292
15939
  * Return true if the provided path string is an absolute path
@@ -15314,8 +15961,8 @@ var init_esm4 = __esm({
15314
15961
  /**
15315
15962
  * @internal
15316
15963
  */
15317
- newRoot(fs6) {
15318
- return new PathPosix(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs6 });
15964
+ newRoot(fs7) {
15965
+ return new PathPosix(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs7 });
15319
15966
  }
15320
15967
  /**
15321
15968
  * Return true if the provided path string is an absolute path
@@ -16455,7 +17102,7 @@ import { exec as exec5 } from "child_process";
16455
17102
  import { promisify as promisify7 } from "util";
16456
17103
  import { randomUUID as randomUUID2 } from "crypto";
16457
17104
  import { EventEmitter as EventEmitter2 } from "events";
16458
- import fs5 from "fs";
17105
+ import fs6 from "fs";
16459
17106
  import { promises as fsPromises } from "fs";
16460
17107
  import path6 from "path";
16461
17108
  function isSessionCancelled(sessionId) {
@@ -16515,6 +17162,20 @@ function createWrappedTools(baseTools) {
16515
17162
  baseTools.bashTool.execute
16516
17163
  );
16517
17164
  }
17165
+ if (baseTools.editTool) {
17166
+ wrappedTools.editToolInstance = wrapToolWithEmitter(
17167
+ baseTools.editTool,
17168
+ "edit",
17169
+ baseTools.editTool.execute
17170
+ );
17171
+ }
17172
+ if (baseTools.createTool) {
17173
+ wrappedTools.createToolInstance = wrapToolWithEmitter(
17174
+ baseTools.createTool,
17175
+ "create",
17176
+ baseTools.createTool.execute
17177
+ );
17178
+ }
16518
17179
  return wrappedTools;
16519
17180
  }
16520
17181
  var toolCallEmitter, activeToolExecutions, wrapToolWithEmitter, listFilesTool, searchFilesTool, listFilesToolInstance, searchFilesToolInstance;
@@ -16525,9 +17186,9 @@ var init_probeTool = __esm({
16525
17186
  init_esm5();
16526
17187
  toolCallEmitter = new EventEmitter2();
16527
17188
  activeToolExecutions = /* @__PURE__ */ new Map();
16528
- wrapToolWithEmitter = (tool3, toolName, baseExecute) => {
17189
+ wrapToolWithEmitter = (tool4, toolName, baseExecute) => {
16529
17190
  return {
16530
- ...tool3,
17191
+ ...tool4,
16531
17192
  // Spread schema, description etc.
16532
17193
  execute: async (params) => {
16533
17194
  const debug = process.env.DEBUG === "1";
@@ -16762,8 +17423,10 @@ var init_index = __esm({
16762
17423
  init_file_lister();
16763
17424
  init_system_message();
16764
17425
  init_common();
17426
+ init_edit();
16765
17427
  init_vercel();
16766
17428
  init_bash();
17429
+ init_edit();
16767
17430
  init_ProbeAgent();
16768
17431
  init_simpleTelemetry();
16769
17432
  init_probeTool();
@@ -16796,10 +17459,22 @@ function extractThinkingContent(xmlString) {
16796
17459
  return thinkingMatch ? thinkingMatch[1].trim() : null;
16797
17460
  }
16798
17461
  function checkAttemptCompleteRecovery(cleanedXmlString, validTools = []) {
16799
- const attemptCompletionMatch = cleanedXmlString.match(/<attempt_completion>([\s\S]*?)(?:<\/attempt_completion>|$)/);
16800
- if (attemptCompletionMatch) {
16801
- const content = attemptCompletionMatch[1].trim();
16802
- const hasClosingTag = cleanedXmlString.includes("</attempt_completion>");
17462
+ const openTagIndex = cleanedXmlString.indexOf("<attempt_completion>");
17463
+ if (openTagIndex !== -1) {
17464
+ const afterOpenTag = cleanedXmlString.substring(openTagIndex + "<attempt_completion>".length);
17465
+ const closeTagIndex = cleanedXmlString.lastIndexOf("</attempt_completion>");
17466
+ let content;
17467
+ let hasClosingTag = false;
17468
+ if (closeTagIndex !== -1 && closeTagIndex >= openTagIndex + "<attempt_completion>".length) {
17469
+ content = cleanedXmlString.substring(
17470
+ openTagIndex + "<attempt_completion>".length,
17471
+ closeTagIndex
17472
+ ).trim();
17473
+ hasClosingTag = true;
17474
+ } else {
17475
+ content = afterOpenTag.trim();
17476
+ hasClosingTag = false;
17477
+ }
16803
17478
  if (content) {
16804
17479
  return {
16805
17480
  toolName: "attempt_completion",
@@ -16845,8 +17520,8 @@ function checkAttemptCompleteRecovery(cleanedXmlString, validTools = []) {
16845
17520
  function hasOtherToolTags(xmlString, validTools = []) {
16846
17521
  const defaultTools = ["search", "query", "extract", "listFiles", "searchFiles", "implement", "attempt_completion"];
16847
17522
  const toolsToCheck = validTools.length > 0 ? validTools : defaultTools;
16848
- for (const tool3 of toolsToCheck) {
16849
- if (tool3 !== "attempt_completion" && xmlString.includes(`<${tool3}`)) {
17523
+ for (const tool4 of toolsToCheck) {
17524
+ if (tool4 !== "attempt_completion" && xmlString.includes(`<${tool4}`)) {
16850
17525
  return true;
16851
17526
  }
16852
17527
  }
@@ -16884,6 +17559,10 @@ function createTools(configOptions) {
16884
17559
  if (configOptions.enableBash) {
16885
17560
  tools2.bashTool = bashTool(configOptions);
16886
17561
  }
17562
+ if (configOptions.allowEdit) {
17563
+ tools2.editTool = editTool(configOptions);
17564
+ tools2.createTool = createTool(configOptions);
17565
+ }
16887
17566
  return tools2;
16888
17567
  }
16889
17568
  function parseXmlToolCallWithThinking(xmlString, validTools) {
@@ -16984,7 +17663,7 @@ function createMockProvider() {
16984
17663
  provider: "mock",
16985
17664
  // Mock the doGenerate method used by Vercel AI SDK
16986
17665
  doGenerate: async ({ messages, tools: tools2 }) => {
16987
- await new Promise((resolve5) => setTimeout(resolve5, 10));
17666
+ await new Promise((resolve6) => setTimeout(resolve6, 10));
16988
17667
  return {
16989
17668
  text: "This is a mock response for testing",
16990
17669
  toolCalls: [],
@@ -28971,16 +29650,6 @@ var init_parser2 = __esm({
28971
29650
  this.subgraph = this.RULE("subgraph", () => {
28972
29651
  this.CONSUME(SubgraphKeyword);
28973
29652
  this.OR([
28974
- {
28975
- ALT: () => {
28976
- this.CONSUME(Identifier, { LABEL: "subgraphId" });
28977
- this.OPTION(() => {
28978
- this.CONSUME1(SquareOpen);
28979
- this.SUBRULE(this.nodeContent);
28980
- this.CONSUME1(SquareClose);
28981
- });
28982
- }
28983
- },
28984
29653
  {
28985
29654
  ALT: () => {
28986
29655
  this.CONSUME(QuotedString, { LABEL: "subgraphTitleQ" });
@@ -28992,6 +29661,33 @@ var init_parser2 = __esm({
28992
29661
  this.SUBRULE2(this.nodeContent);
28993
29662
  this.CONSUME2(SquareClose);
28994
29663
  }
29664
+ },
29665
+ {
29666
+ ALT: () => {
29667
+ this.CONSUME1(Identifier, { LABEL: "subgraphIdOrFirstWord" });
29668
+ this.OPTION(() => {
29669
+ this.OR1([
29670
+ {
29671
+ ALT: () => {
29672
+ this.CONSUME1(SquareOpen);
29673
+ this.SUBRULE(this.nodeContent);
29674
+ this.CONSUME1(SquareClose);
29675
+ }
29676
+ },
29677
+ {
29678
+ ALT: () => {
29679
+ this.AT_LEAST_ONE(() => {
29680
+ this.OR2([
29681
+ { ALT: () => this.CONSUME2(Identifier) },
29682
+ { ALT: () => this.CONSUME(Text) },
29683
+ { ALT: () => this.CONSUME(NumberLiteral) }
29684
+ ]);
29685
+ });
29686
+ }
29687
+ }
29688
+ ]);
29689
+ });
29690
+ }
28995
29691
  }
28996
29692
  ]);
28997
29693
  this.CONSUME(Newline);
@@ -29595,7 +30291,6 @@ var init_semantics = __esm({
29595
30291
  const last2 = allChildren[allChildren.length - 1];
29596
30292
  const isSlash = (t) => t.image === "/" || t.image === "\\";
29597
30293
  if (isSlash(first2) && isSlash(last2)) {
29598
- continue;
29599
30294
  }
29600
30295
  }
29601
30296
  const opens = ch.RoundOpen || [];
@@ -30389,16 +31084,19 @@ function mapFlowchartParserError(err, text) {
30389
31084
  const subgraphIdx = lineStr.indexOf("subgraph");
30390
31085
  if (subgraphIdx !== -1) {
30391
31086
  const afterSubgraph = lineStr.slice(subgraphIdx + 8).trim();
30392
- if (afterSubgraph && !afterSubgraph.startsWith('"') && !afterSubgraph.startsWith("'") && afterSubgraph.includes(" ")) {
30393
- return {
30394
- line,
30395
- column,
30396
- severity: "error",
30397
- code: "FL-SUBGRAPH-UNQUOTED-TITLE",
30398
- message: "Subgraph titles with spaces must be quoted.",
30399
- hint: 'Example: subgraph "Existing Logic Path" or use underscores: subgraph Existing_Logic_Path',
30400
- length: afterSubgraph.length
30401
- };
31087
+ if (afterSubgraph && !afterSubgraph.startsWith('"') && !afterSubgraph.startsWith("'")) {
31088
+ const hasHazard = /[\[\](){}/:|"'\\]/.test(afterSubgraph);
31089
+ if (hasHazard) {
31090
+ return {
31091
+ line,
31092
+ column,
31093
+ severity: "error",
31094
+ code: "FL-SUBGRAPH-UNQUOTED-TITLE",
31095
+ message: "Subgraph title contains special characters; wrap it in quotes.",
31096
+ hint: 'Example: subgraph "Streams (inside Gateway)"',
31097
+ length: afterSubgraph.length
31098
+ };
31099
+ }
30402
31100
  }
30403
31101
  }
30404
31102
  }
@@ -31096,11 +31794,11 @@ function validateFlowchart(text, options = {}) {
31096
31794
  const byLine = /* @__PURE__ */ new Map();
31097
31795
  const collect = (arr) => {
31098
31796
  for (const e of arr || []) {
31099
- if (e && e.code === "FL-LABEL-PARENS-UNQUOTED") {
31797
+ if (e && (e.code === "FL-LABEL-PARENS-UNQUOTED" || e.code === "FL-LABEL-AT-IN-UNQUOTED")) {
31100
31798
  const ln = e.line ?? 0;
31101
31799
  const col = e.column ?? 1;
31102
31800
  const list = byLine.get(ln) || [];
31103
- list.push(col);
31801
+ list.push({ start: col, end: col });
31104
31802
  byLine.set(ln, list);
31105
31803
  }
31106
31804
  }
@@ -31109,33 +31807,91 @@ function validateFlowchart(text, options = {}) {
31109
31807
  collect(errs);
31110
31808
  const lines2 = text2.split(/\r?\n/);
31111
31809
  for (let ii = 0; ii < lines2.length; ii++) {
31112
- const raw2 = lines2[ii] || "";
31113
- if (!raw2.includes("[") || !raw2.includes("]"))
31810
+ const raw = lines2[ii] || "";
31811
+ if (!raw.includes("[") || !raw.includes("]"))
31114
31812
  continue;
31115
- let search2 = 0;
31116
- while (true) {
31117
- const open2 = raw2.indexOf("[", search2);
31118
- if (open2 === -1)
31119
- break;
31120
- const close2 = raw2.indexOf("]", open2 + 1);
31121
- if (close2 === -1)
31122
- break;
31123
- const seg2 = raw2.slice(open2 + 1, close2);
31124
- const trimmed2 = seg2.trim();
31125
- const ln2 = ii + 1;
31126
- const lsp = trimmed2.slice(0, 1);
31127
- const rsp = trimmed2.slice(-1);
31128
- const isSlashPair = (lsp === "/" || lsp === "\\") && (rsp === "/" || rsp === "\\");
31129
- const isParenWrapped = lsp === "(" && rsp === ")";
31130
- const segStartCol = open2 + 2;
31131
- const segEndCol = close2 + 1;
31132
- const existing = byLine.get(ln2) || [];
31133
- const covered = existing.some((c) => c >= segStartCol && c <= segEndCol);
31134
- if (!covered && !/^".*"$/.test(trimmed2) && (seg2.includes("(") || seg2.includes(")")) && !isSlashPair && !isParenWrapped) {
31135
- errs.push({ line: ln2, column: segStartCol, severity: "error", code: "FL-LABEL-PARENS-UNQUOTED", message: "Parentheses inside an unquoted label are not supported by Mermaid.", hint: 'Wrap the label in quotes, e.g., A["Mark (X)"] \u2014 or replace ( and ) with HTML entities: &#40; and &#41;.' });
31136
- byLine.set(ln2, existing.concat([segStartCol]));
31813
+ let i = 0;
31814
+ const n = raw.length;
31815
+ let inQuote = false;
31816
+ let esc = false;
31817
+ while (i < n) {
31818
+ const ch = raw[i];
31819
+ if (inQuote) {
31820
+ if (esc) {
31821
+ esc = false;
31822
+ } else if (ch === "\\") {
31823
+ esc = true;
31824
+ } else if (ch === '"') {
31825
+ inQuote = false;
31826
+ }
31827
+ i++;
31828
+ continue;
31829
+ }
31830
+ if (ch === '"') {
31831
+ inQuote = true;
31832
+ i++;
31833
+ continue;
31834
+ }
31835
+ if (ch === "[") {
31836
+ let j = i + 1;
31837
+ let inQ = false;
31838
+ let esc2 = false;
31839
+ let depth = 1;
31840
+ while (j < n && depth > 0) {
31841
+ const cj = raw[j];
31842
+ if (inQ) {
31843
+ if (esc2) {
31844
+ esc2 = false;
31845
+ } else if (cj === "\\") {
31846
+ esc2 = true;
31847
+ } else if (cj === '"') {
31848
+ inQ = false;
31849
+ }
31850
+ j++;
31851
+ continue;
31852
+ }
31853
+ if (cj === '"') {
31854
+ inQ = true;
31855
+ j++;
31856
+ continue;
31857
+ }
31858
+ if (cj === "[")
31859
+ depth++;
31860
+ else if (cj === "]")
31861
+ depth--;
31862
+ j++;
31863
+ }
31864
+ if (depth === 0) {
31865
+ const startCol = i + 2;
31866
+ const endCol = j;
31867
+ const seg = raw.slice(i + 1, j - 1);
31868
+ const trimmed = seg.trim();
31869
+ const ln = ii + 1;
31870
+ const lsp = trimmed.slice(0, 1), rsp = trimmed.slice(-1);
31871
+ const isSlashPair = (lsp === "/" || lsp === "\\") && (rsp === "/" || rsp === "\\");
31872
+ const isParenWrapped = lsp === "(" && rsp === ")";
31873
+ const isQuoted = /^"[\s\S]*"$/.test(trimmed);
31874
+ const existing = byLine.get(ln) || [];
31875
+ const covered = existing.some((r) => !(endCol < r.start || startCol > r.end));
31876
+ const hasParens = seg.includes("(") || seg.includes(")");
31877
+ const hasAt = seg.includes("@");
31878
+ if (!covered && !isQuoted && !isParenWrapped && hasParens) {
31879
+ errs.push({ line: ln, column: startCol, severity: "error", code: "FL-LABEL-PARENS-UNQUOTED", message: "Parentheses inside an unquoted label are not supported by Mermaid.", hint: 'Wrap the label in quotes, e.g., A["Mark (X)"] \u2014 or replace ( and ) with HTML entities: &#40; and &#41;.' });
31880
+ existing.push({ start: startCol, end: endCol });
31881
+ byLine.set(ln, existing);
31882
+ }
31883
+ if (!covered && !isQuoted && !isSlashPair && hasAt) {
31884
+ errs.push({ line: ln, column: startCol, severity: "error", code: "FL-LABEL-AT-IN-UNQUOTED", message: "'@' inside an unquoted label can be misparsed by Mermaid.", hint: 'Wrap the label in quotes, e.g., B["@probelabs/probe v0.6.0-rc149"]' });
31885
+ existing.push({ start: startCol, end: endCol });
31886
+ byLine.set(ln, existing);
31887
+ }
31888
+ i = j;
31889
+ continue;
31890
+ } else {
31891
+ break;
31892
+ }
31137
31893
  }
31138
- search2 = close2 + 1;
31894
+ i++;
31139
31895
  }
31140
31896
  }
31141
31897
  }
@@ -42621,7 +43377,7 @@ var require_bk = __commonJS({
42621
43377
  return xs;
42622
43378
  }
42623
43379
  function buildBlockGraph(g, layering, root2, reverseSep) {
42624
- var blockGraph = new Graph(), graphLabel = g.graph(), sepFn = sep2(graphLabel.nodesep, graphLabel.edgesep, reverseSep);
43380
+ var blockGraph = new Graph(), graphLabel = g.graph(), sepFn = sep3(graphLabel.nodesep, graphLabel.edgesep, reverseSep);
42625
43381
  _.forEach(layering, function(layer) {
42626
43382
  var u;
42627
43383
  _.forEach(layer, function(v) {
@@ -42711,7 +43467,7 @@ var require_bk = __commonJS({
42711
43467
  alignCoordinates(xss, smallestWidth);
42712
43468
  return balance(xss, g.graph().align);
42713
43469
  }
42714
- function sep2(nodeSep, edgeSep, reverseSep) {
43470
+ function sep3(nodeSep, edgeSep, reverseSep) {
42715
43471
  return function(g, v, w) {
42716
43472
  var vLabel = g.node(v);
42717
43473
  var wLabel = g.node(w);
@@ -50005,7 +50761,7 @@ var require_compile = __commonJS({
50005
50761
  const schOrFunc = root2.refs[ref];
50006
50762
  if (schOrFunc)
50007
50763
  return schOrFunc;
50008
- let _sch = resolve5.call(this, root2, ref);
50764
+ let _sch = resolve6.call(this, root2, ref);
50009
50765
  if (_sch === void 0) {
50010
50766
  const schema = (_a = root2.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
50011
50767
  const { schemaId } = this.opts;
@@ -50032,7 +50788,7 @@ var require_compile = __commonJS({
50032
50788
  function sameSchemaEnv(s1, s2) {
50033
50789
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
50034
50790
  }
50035
- function resolve5(root2, ref) {
50791
+ function resolve6(root2, ref) {
50036
50792
  let sch;
50037
50793
  while (typeof (sch = this.refs[ref]) == "string")
50038
50794
  ref = sch;
@@ -50607,7 +51363,7 @@ var require_fast_uri = __commonJS({
50607
51363
  }
50608
51364
  return uri;
50609
51365
  }
50610
- function resolve5(baseURI, relativeURI, options) {
51366
+ function resolve6(baseURI, relativeURI, options) {
50611
51367
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
50612
51368
  const resolved = resolveComponent(parse6(baseURI, schemelessOptions), parse6(relativeURI, schemelessOptions), schemelessOptions, true);
50613
51369
  schemelessOptions.skipEscape = true;
@@ -50834,7 +51590,7 @@ var require_fast_uri = __commonJS({
50834
51590
  var fastUri = {
50835
51591
  SCHEMES,
50836
51592
  normalize: normalize2,
50837
- resolve: resolve5,
51593
+ resolve: resolve6,
50838
51594
  resolveComponent,
50839
51595
  equal,
50840
51596
  serialize,
@@ -54852,15 +55608,15 @@ Provide only the corrected Mermaid diagram within a mermaid code block. Do not a
54852
55608
  });
54853
55609
 
54854
55610
  // src/agent/mcp/config.js
54855
- import { readFileSync, existsSync as existsSync3, mkdirSync as mkdirSync2, writeFileSync } from "fs";
54856
- import { join as join2, dirname as dirname2 } from "path";
55611
+ import { readFileSync, existsSync as existsSync4, mkdirSync as mkdirSync2, writeFileSync } from "fs";
55612
+ import { join as join2, dirname as dirname3 } from "path";
54857
55613
  import { homedir } from "os";
54858
55614
  import { fileURLToPath as fileURLToPath6 } from "url";
54859
55615
  function loadMCPConfigurationFromPath(configPath) {
54860
55616
  if (!configPath) {
54861
55617
  throw new Error("Config path is required");
54862
55618
  }
54863
- if (!existsSync3(configPath)) {
55619
+ if (!existsSync4(configPath)) {
54864
55620
  throw new Error(`MCP configuration file not found: ${configPath}`);
54865
55621
  }
54866
55622
  try {
@@ -54889,7 +55645,7 @@ function loadMCPConfiguration() {
54889
55645
  ].filter(Boolean);
54890
55646
  let config = null;
54891
55647
  for (const configPath of configPaths) {
54892
- if (existsSync3(configPath)) {
55648
+ if (existsSync4(configPath)) {
54893
55649
  try {
54894
55650
  const content = readFileSync(configPath, "utf8");
54895
55651
  config = JSON.parse(content);
@@ -54992,7 +55748,7 @@ var init_config = __esm({
54992
55748
  "src/agent/mcp/config.js"() {
54993
55749
  "use strict";
54994
55750
  __filename4 = fileURLToPath6(import.meta.url);
54995
- __dirname4 = dirname2(__filename4);
55751
+ __dirname4 = dirname3(__filename4);
54996
55752
  DEFAULT_CONFIG = {
54997
55753
  mcpServers: {
54998
55754
  // Example probe server configuration
@@ -55181,12 +55937,12 @@ var init_client = __esm({
55181
55937
  const toolsResponse = await client.listTools();
55182
55938
  const toolCount = toolsResponse?.tools?.length || 0;
55183
55939
  if (toolsResponse && toolsResponse.tools) {
55184
- for (const tool3 of toolsResponse.tools) {
55185
- const qualifiedName = `${name}_${tool3.name}`;
55940
+ for (const tool4 of toolsResponse.tools) {
55941
+ const qualifiedName = `${name}_${tool4.name}`;
55186
55942
  this.tools.set(qualifiedName, {
55187
- ...tool3,
55943
+ ...tool4,
55188
55944
  serverName: name,
55189
- originalName: tool3.name
55945
+ originalName: tool4.name
55190
55946
  });
55191
55947
  if (this.debug) {
55192
55948
  console.error(`[MCP DEBUG] Registered tool: ${qualifiedName}`);
@@ -55209,13 +55965,13 @@ var init_client = __esm({
55209
55965
  * @param {Object} args - Tool arguments
55210
55966
  */
55211
55967
  async callTool(toolName, args) {
55212
- const tool3 = this.tools.get(toolName);
55213
- if (!tool3) {
55968
+ const tool4 = this.tools.get(toolName);
55969
+ if (!tool4) {
55214
55970
  throw new Error(`Unknown tool: ${toolName}`);
55215
55971
  }
55216
- const clientInfo = this.clients.get(tool3.serverName);
55972
+ const clientInfo = this.clients.get(tool4.serverName);
55217
55973
  if (!clientInfo) {
55218
- throw new Error(`Server ${tool3.serverName} not connected`);
55974
+ throw new Error(`Server ${tool4.serverName} not connected`);
55219
55975
  }
55220
55976
  try {
55221
55977
  if (this.debug) {
@@ -55229,7 +55985,7 @@ var init_client = __esm({
55229
55985
  });
55230
55986
  const result = await Promise.race([
55231
55987
  clientInfo.client.callTool({
55232
- name: tool3.originalName,
55988
+ name: tool4.originalName,
55233
55989
  arguments: args
55234
55990
  }),
55235
55991
  timeoutPromise
@@ -55252,11 +56008,11 @@ var init_client = __esm({
55252
56008
  */
55253
56009
  getTools() {
55254
56010
  const tools2 = {};
55255
- for (const [name, tool3] of this.tools.entries()) {
56011
+ for (const [name, tool4] of this.tools.entries()) {
55256
56012
  tools2[name] = {
55257
- description: tool3.description,
55258
- inputSchema: tool3.inputSchema,
55259
- serverName: tool3.serverName
56013
+ description: tool4.description,
56014
+ inputSchema: tool4.inputSchema,
56015
+ serverName: tool4.serverName
55260
56016
  };
55261
56017
  }
55262
56018
  return tools2;
@@ -55267,10 +56023,10 @@ var init_client = __esm({
55267
56023
  */
55268
56024
  getVercelTools() {
55269
56025
  const tools2 = {};
55270
- for (const [name, tool3] of this.tools.entries()) {
56026
+ for (const [name, tool4] of this.tools.entries()) {
55271
56027
  tools2[name] = {
55272
- description: tool3.description,
55273
- inputSchema: tool3.inputSchema,
56028
+ description: tool4.description,
56029
+ inputSchema: tool4.inputSchema,
55274
56030
  execute: async (args) => {
55275
56031
  const result = await this.callTool(name, args);
55276
56032
  if (result.content && result.content[0]) {
@@ -55319,9 +56075,9 @@ var init_client = __esm({
55319
56075
  });
55320
56076
 
55321
56077
  // src/agent/mcp/xmlBridge.js
55322
- function mcpToolToXmlDefinition(name, tool3) {
55323
- const description = tool3.description || "MCP tool";
55324
- const inputSchema = tool3.inputSchema || tool3.parameters || {};
56078
+ function mcpToolToXmlDefinition(name, tool4) {
56079
+ const description = tool4.description || "MCP tool";
56080
+ const inputSchema = tool4.inputSchema || tool4.parameters || {};
55325
56081
  let paramDocs = "";
55326
56082
  if (inputSchema.properties) {
55327
56083
  paramDocs = "\n\nParameters (provide as JSON object):";
@@ -55500,8 +56256,8 @@ var init_xmlBridge = __esm({
55500
56256
  const vercelTools = this.mcpManager.getVercelTools();
55501
56257
  this.mcpTools = vercelTools;
55502
56258
  const toolCount = Object.keys(vercelTools).length;
55503
- for (const [name, tool3] of Object.entries(vercelTools)) {
55504
- this.xmlDefinitions[name] = mcpToolToXmlDefinition(name, tool3);
56259
+ for (const [name, tool4] of Object.entries(vercelTools)) {
56260
+ this.xmlDefinitions[name] = mcpToolToXmlDefinition(name, tool4);
55505
56261
  }
55506
56262
  if (toolCount === 0) {
55507
56263
  console.error("[MCP INFO] MCP initialization complete: 0 tools loaded");
@@ -55548,14 +56304,14 @@ var init_xmlBridge = __esm({
55548
56304
  console.error(`[MCP DEBUG] Executing MCP tool: ${toolName}`);
55549
56305
  console.error(`[MCP DEBUG] Parameters:`, JSON.stringify(params, null, 2));
55550
56306
  }
55551
- const tool3 = this.mcpTools[toolName];
55552
- if (!tool3) {
56307
+ const tool4 = this.mcpTools[toolName];
56308
+ if (!tool4) {
55553
56309
  console.error(`[MCP ERROR] Unknown MCP tool: ${toolName}`);
55554
56310
  console.error(`[MCP ERROR] Available tools: ${this.getToolNames().join(", ")}`);
55555
56311
  throw new Error(`Unknown MCP tool: ${toolName}`);
55556
56312
  }
55557
56313
  try {
55558
- const result = await tool3.execute(params);
56314
+ const result = await tool4.execute(params);
55559
56315
  if (this.debug) {
55560
56316
  console.error(`[MCP DEBUG] Tool ${toolName} executed successfully`);
55561
56317
  }
@@ -55609,22 +56365,800 @@ var init_mcp = __esm({
55609
56365
  }
55610
56366
  });
55611
56367
 
56368
+ // src/agent/RetryManager.js
56369
+ function isRetryableError(error, retryableErrors = DEFAULT_RETRYABLE_ERRORS) {
56370
+ if (!error) return false;
56371
+ const errorString = error.toString().toLowerCase();
56372
+ const errorMessage = (error.message || "").toLowerCase();
56373
+ const errorCode = (error.code || "").toLowerCase();
56374
+ const errorType = (error.type || "").toLowerCase();
56375
+ const statusCode = error.statusCode || error.status;
56376
+ for (const pattern of retryableErrors) {
56377
+ const lowerPattern = pattern.toLowerCase();
56378
+ if (errorString.includes(lowerPattern) || errorMessage.includes(lowerPattern) || errorCode.includes(lowerPattern) || errorType.includes(lowerPattern) || statusCode?.toString() === pattern) {
56379
+ return true;
56380
+ }
56381
+ }
56382
+ return false;
56383
+ }
56384
+ function extractErrorInfo(error) {
56385
+ return {
56386
+ message: error.message || error.toString(),
56387
+ type: error.type || error.constructor.name,
56388
+ code: error.code,
56389
+ statusCode: error.statusCode || error.status,
56390
+ provider: error.provider,
56391
+ isRetryable: isRetryableError(error)
56392
+ };
56393
+ }
56394
+ function sleep(ms) {
56395
+ return new Promise((resolve6) => setTimeout(resolve6, ms));
56396
+ }
56397
+ var DEFAULT_RETRYABLE_ERRORS, RetryManager;
56398
+ var init_RetryManager = __esm({
56399
+ "src/agent/RetryManager.js"() {
56400
+ "use strict";
56401
+ DEFAULT_RETRYABLE_ERRORS = [
56402
+ "Overloaded",
56403
+ "overloaded",
56404
+ "rate_limit",
56405
+ "rate limit",
56406
+ "429",
56407
+ "500",
56408
+ "502",
56409
+ "503",
56410
+ "504",
56411
+ "timeout",
56412
+ "ECONNRESET",
56413
+ "ETIMEDOUT",
56414
+ "ENOTFOUND",
56415
+ "api_error"
56416
+ ];
56417
+ RetryManager = class {
56418
+ /**
56419
+ * Create a new RetryManager
56420
+ * @param {Object} options - Configuration options
56421
+ * @param {number} [options.maxRetries=3] - Maximum retry attempts
56422
+ * @param {number} [options.initialDelay=1000] - Initial delay in ms (1 second)
56423
+ * @param {number} [options.maxDelay=30000] - Maximum delay in ms (30 seconds)
56424
+ * @param {number} [options.backoffFactor=2] - Exponential backoff multiplier
56425
+ * @param {Array<string>} [options.retryableErrors] - List of retryable error patterns
56426
+ * @param {boolean} [options.debug=false] - Enable debug logging
56427
+ */
56428
+ constructor(options = {}) {
56429
+ this.maxRetries = this._validateNumber(options.maxRetries, 3, "maxRetries", 0, 100);
56430
+ this.initialDelay = this._validateNumber(options.initialDelay, 1e3, "initialDelay", 0, 6e4);
56431
+ this.maxDelay = this._validateNumber(options.maxDelay, 3e4, "maxDelay", 0, 3e5);
56432
+ this.backoffFactor = this._validateNumber(options.backoffFactor, 2, "backoffFactor", 1, 10);
56433
+ this.retryableErrors = options.retryableErrors || DEFAULT_RETRYABLE_ERRORS;
56434
+ this.debug = options.debug ?? false;
56435
+ this.jitter = options.jitter ?? true;
56436
+ if (this.maxDelay < this.initialDelay) {
56437
+ throw new Error("maxDelay must be greater than or equal to initialDelay");
56438
+ }
56439
+ this.stats = {
56440
+ totalAttempts: 0,
56441
+ totalRetries: 0,
56442
+ successfulRetries: 0,
56443
+ failedRetries: 0
56444
+ };
56445
+ }
56446
+ /**
56447
+ * Validate a numeric parameter
56448
+ * @param {*} value - Value to validate
56449
+ * @param {number} defaultValue - Default if undefined
56450
+ * @param {string} name - Parameter name for error messages
56451
+ * @param {number} min - Minimum allowed value
56452
+ * @param {number} max - Maximum allowed value
56453
+ * @returns {number} - Validated number
56454
+ * @private
56455
+ */
56456
+ _validateNumber(value, defaultValue, name, min, max) {
56457
+ if (value === void 0 || value === null) {
56458
+ return defaultValue;
56459
+ }
56460
+ const num = Number(value);
56461
+ if (isNaN(num)) {
56462
+ throw new Error(`${name} must be a number, got: ${value}`);
56463
+ }
56464
+ if (num < min || num > max) {
56465
+ throw new Error(`${name} must be between ${min} and ${max}, got: ${num}`);
56466
+ }
56467
+ return num;
56468
+ }
56469
+ /**
56470
+ * Execute a function with retry logic
56471
+ * @param {Function} fn - Async function to execute
56472
+ * @param {Object} [context={}] - Context information for logging
56473
+ * @param {AbortSignal} [context.signal] - Optional abort signal for cancellation
56474
+ * @returns {Promise<*>} - Result from the function
56475
+ * @throws {Error} - If all retries are exhausted or operation is aborted
56476
+ */
56477
+ async executeWithRetry(fn, context = {}) {
56478
+ let lastError = null;
56479
+ let currentDelay = this.initialDelay;
56480
+ for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
56481
+ if (context.signal?.aborted) {
56482
+ const abortError = new Error("Operation aborted");
56483
+ abortError.name = "AbortError";
56484
+ throw abortError;
56485
+ }
56486
+ this.stats.totalAttempts++;
56487
+ try {
56488
+ if (this.debug && attempt > 0) {
56489
+ console.log(`[RetryManager] Retry attempt ${attempt}/${this.maxRetries}`, context);
56490
+ }
56491
+ const result = await fn();
56492
+ if (attempt > 0) {
56493
+ this.stats.successfulRetries++;
56494
+ if (this.debug) {
56495
+ console.log(`[RetryManager] \u2705 Retry successful on attempt ${attempt + 1}`, context);
56496
+ }
56497
+ }
56498
+ return result;
56499
+ } catch (error) {
56500
+ lastError = error;
56501
+ const errorInfo = extractErrorInfo(error);
56502
+ const shouldRetry = isRetryableError(error, this.retryableErrors);
56503
+ const hasRetriesLeft = attempt < this.maxRetries;
56504
+ if (this.debug) {
56505
+ console.log(`[RetryManager] \u274C Attempt ${attempt + 1}/${this.maxRetries + 1} failed:`, {
56506
+ ...context,
56507
+ error: errorInfo,
56508
+ shouldRetry,
56509
+ hasRetriesLeft
56510
+ });
56511
+ }
56512
+ if (!shouldRetry) {
56513
+ if (this.debug) {
56514
+ console.log(`[RetryManager] Error is not retryable, failing immediately`, errorInfo);
56515
+ }
56516
+ throw error;
56517
+ }
56518
+ if (!hasRetriesLeft) {
56519
+ this.stats.failedRetries++;
56520
+ if (this.debug) {
56521
+ console.log(`[RetryManager] Max retries (${this.maxRetries}) exhausted`, context);
56522
+ }
56523
+ throw error;
56524
+ }
56525
+ this.stats.totalRetries++;
56526
+ let delayWithJitter = currentDelay;
56527
+ if (this.jitter) {
56528
+ const jitterAmount = currentDelay * 0.25;
56529
+ delayWithJitter = currentDelay + (Math.random() * jitterAmount * 2 - jitterAmount);
56530
+ }
56531
+ if (this.debug) {
56532
+ console.log(`[RetryManager] Waiting ${Math.round(delayWithJitter)}ms before retry...`);
56533
+ }
56534
+ await sleep(delayWithJitter);
56535
+ currentDelay = Math.min(currentDelay * this.backoffFactor, this.maxDelay);
56536
+ }
56537
+ }
56538
+ throw lastError;
56539
+ }
56540
+ /**
56541
+ * Check if an error is retryable
56542
+ * @param {Error} error - The error to check
56543
+ * @returns {boolean} - True if error should be retried
56544
+ */
56545
+ isRetryable(error) {
56546
+ return isRetryableError(error, this.retryableErrors);
56547
+ }
56548
+ /**
56549
+ * Get retry statistics
56550
+ * @returns {Object} - Statistics object
56551
+ */
56552
+ getStats() {
56553
+ return { ...this.stats };
56554
+ }
56555
+ /**
56556
+ * Reset statistics
56557
+ */
56558
+ resetStats() {
56559
+ this.stats = {
56560
+ totalAttempts: 0,
56561
+ totalRetries: 0,
56562
+ successfulRetries: 0,
56563
+ failedRetries: 0
56564
+ };
56565
+ }
56566
+ };
56567
+ }
56568
+ });
56569
+
56570
+ // src/agent/FallbackManager.js
56571
+ import { createAnthropic } from "@ai-sdk/anthropic";
56572
+ import { createOpenAI } from "@ai-sdk/openai";
56573
+ import { createGoogleGenerativeAI } from "@ai-sdk/google";
56574
+ import { createAmazonBedrock } from "@ai-sdk/amazon-bedrock";
56575
+ function createFallbackManagerFromEnv(debug = false) {
56576
+ const fallbackProvidersEnv = process.env.FALLBACK_PROVIDERS;
56577
+ const fallbackModelsEnv = process.env.FALLBACK_MODELS;
56578
+ if (!fallbackProvidersEnv && !fallbackModelsEnv) {
56579
+ return null;
56580
+ }
56581
+ let providers = [];
56582
+ let models = [];
56583
+ let strategy = FALLBACK_STRATEGIES.ANY;
56584
+ if (fallbackProvidersEnv) {
56585
+ try {
56586
+ if (typeof fallbackProvidersEnv !== "string" || fallbackProvidersEnv.length > 1e4) {
56587
+ console.error("[FallbackManager] FALLBACK_PROVIDERS must be a valid JSON string under 10KB");
56588
+ return null;
56589
+ }
56590
+ const parsed = JSON.parse(fallbackProvidersEnv);
56591
+ if (!Array.isArray(parsed)) {
56592
+ console.error("[FallbackManager] FALLBACK_PROVIDERS must be a JSON array");
56593
+ return null;
56594
+ }
56595
+ providers = parsed;
56596
+ strategy = FALLBACK_STRATEGIES.CUSTOM;
56597
+ } catch (error) {
56598
+ console.error("[FallbackManager] Failed to parse FALLBACK_PROVIDERS:", error.message);
56599
+ return null;
56600
+ }
56601
+ }
56602
+ if (fallbackModelsEnv) {
56603
+ try {
56604
+ if (typeof fallbackModelsEnv !== "string" || fallbackModelsEnv.length > 1e4) {
56605
+ console.error("[FallbackManager] FALLBACK_MODELS must be a valid JSON string under 10KB");
56606
+ return null;
56607
+ }
56608
+ const parsed = JSON.parse(fallbackModelsEnv);
56609
+ if (!Array.isArray(parsed)) {
56610
+ console.error("[FallbackManager] FALLBACK_MODELS must be a JSON array");
56611
+ return null;
56612
+ }
56613
+ models = parsed;
56614
+ strategy = FALLBACK_STRATEGIES.SAME_PROVIDER;
56615
+ } catch (error) {
56616
+ console.error("[FallbackManager] Failed to parse FALLBACK_MODELS:", error.message);
56617
+ return null;
56618
+ }
56619
+ }
56620
+ const maxTotalAttempts = process.env.FALLBACK_MAX_TOTAL_ATTEMPTS ? (() => {
56621
+ const val = parseInt(process.env.FALLBACK_MAX_TOTAL_ATTEMPTS, 10);
56622
+ if (isNaN(val) || val < 1 || val > 100) {
56623
+ console.warn("[FallbackManager] FALLBACK_MAX_TOTAL_ATTEMPTS must be between 1 and 100, using default: 10");
56624
+ return 10;
56625
+ }
56626
+ return val;
56627
+ })() : 10;
56628
+ return new FallbackManager({
56629
+ strategy,
56630
+ providers,
56631
+ models,
56632
+ maxTotalAttempts,
56633
+ debug
56634
+ });
56635
+ }
56636
+ function buildFallbackProvidersFromEnv(options = {}) {
56637
+ const providers = [];
56638
+ const anthropicApiKey = process.env.ANTHROPIC_API_KEY || process.env.ANTHROPIC_AUTH_TOKEN;
56639
+ const openaiApiKey = process.env.OPENAI_API_KEY;
56640
+ const googleApiKey = process.env.GOOGLE_GENERATIVE_AI_API_KEY || process.env.GOOGLE_API_KEY;
56641
+ const awsAccessKeyId = process.env.AWS_ACCESS_KEY_ID;
56642
+ const awsSecretAccessKey = process.env.AWS_SECRET_ACCESS_KEY;
56643
+ const awsRegion = process.env.AWS_REGION;
56644
+ const awsApiKey = process.env.AWS_BEDROCK_API_KEY;
56645
+ const llmBaseUrl = process.env.LLM_BASE_URL;
56646
+ const anthropicApiUrl = process.env.ANTHROPIC_API_URL || process.env.ANTHROPIC_BASE_URL || llmBaseUrl;
56647
+ const openaiApiUrl = process.env.OPENAI_API_URL || llmBaseUrl;
56648
+ const googleApiUrl = process.env.GOOGLE_API_URL || llmBaseUrl;
56649
+ const awsBedrockBaseUrl = process.env.AWS_BEDROCK_BASE_URL || llmBaseUrl;
56650
+ const primaryProvider = options.primaryProvider?.toLowerCase();
56651
+ const primaryModel = options.primaryModel;
56652
+ if (primaryProvider === "anthropic" && anthropicApiKey) {
56653
+ providers.push({
56654
+ provider: "anthropic",
56655
+ apiKey: anthropicApiKey,
56656
+ ...anthropicApiUrl && { baseURL: anthropicApiUrl },
56657
+ ...primaryModel && { model: primaryModel }
56658
+ });
56659
+ } else if (primaryProvider === "openai" && openaiApiKey) {
56660
+ providers.push({
56661
+ provider: "openai",
56662
+ apiKey: openaiApiKey,
56663
+ ...openaiApiUrl && { baseURL: openaiApiUrl },
56664
+ ...primaryModel && { model: primaryModel }
56665
+ });
56666
+ } else if (primaryProvider === "google" && googleApiKey) {
56667
+ providers.push({
56668
+ provider: "google",
56669
+ apiKey: googleApiKey,
56670
+ ...googleApiUrl && { baseURL: googleApiUrl },
56671
+ ...primaryModel && { model: primaryModel }
56672
+ });
56673
+ } else if (primaryProvider === "bedrock" && (awsAccessKeyId && awsSecretAccessKey && awsRegion || awsApiKey)) {
56674
+ const config = { provider: "bedrock" };
56675
+ if (awsApiKey) {
56676
+ config.apiKey = awsApiKey;
56677
+ } else {
56678
+ config.accessKeyId = awsAccessKeyId;
56679
+ config.secretAccessKey = awsSecretAccessKey;
56680
+ config.region = awsRegion;
56681
+ if (process.env.AWS_SESSION_TOKEN) {
56682
+ config.sessionToken = process.env.AWS_SESSION_TOKEN;
56683
+ }
56684
+ }
56685
+ if (awsBedrockBaseUrl) config.baseURL = awsBedrockBaseUrl;
56686
+ if (primaryModel) config.model = primaryModel;
56687
+ providers.push(config);
56688
+ }
56689
+ if (anthropicApiKey && primaryProvider !== "anthropic") {
56690
+ providers.push({
56691
+ provider: "anthropic",
56692
+ apiKey: anthropicApiKey,
56693
+ ...anthropicApiUrl && { baseURL: anthropicApiUrl }
56694
+ });
56695
+ }
56696
+ if (openaiApiKey && primaryProvider !== "openai") {
56697
+ providers.push({
56698
+ provider: "openai",
56699
+ apiKey: openaiApiKey,
56700
+ ...openaiApiUrl && { baseURL: openaiApiUrl }
56701
+ });
56702
+ }
56703
+ if (googleApiKey && primaryProvider !== "google") {
56704
+ providers.push({
56705
+ provider: "google",
56706
+ apiKey: googleApiKey,
56707
+ ...googleApiUrl && { baseURL: googleApiUrl }
56708
+ });
56709
+ }
56710
+ if ((awsAccessKeyId && awsSecretAccessKey && awsRegion || awsApiKey) && primaryProvider !== "bedrock") {
56711
+ const config = { provider: "bedrock" };
56712
+ if (awsApiKey) {
56713
+ config.apiKey = awsApiKey;
56714
+ } else {
56715
+ config.accessKeyId = awsAccessKeyId;
56716
+ config.secretAccessKey = awsSecretAccessKey;
56717
+ config.region = awsRegion;
56718
+ if (process.env.AWS_SESSION_TOKEN) {
56719
+ config.sessionToken = process.env.AWS_SESSION_TOKEN;
56720
+ }
56721
+ }
56722
+ if (awsBedrockBaseUrl) config.baseURL = awsBedrockBaseUrl;
56723
+ providers.push(config);
56724
+ }
56725
+ return providers;
56726
+ }
56727
+ var FALLBACK_STRATEGIES, DEFAULT_MODELS, FallbackManager;
56728
+ var init_FallbackManager = __esm({
56729
+ "src/agent/FallbackManager.js"() {
56730
+ "use strict";
56731
+ FALLBACK_STRATEGIES = {
56732
+ SAME_MODEL: "same-model",
56733
+ // Try same model on different providers
56734
+ SAME_PROVIDER: "same-provider",
56735
+ // Try different models on same provider
56736
+ ANY: "any",
56737
+ // Try any available provider/model
56738
+ CUSTOM: "custom"
56739
+ // Use custom provider list
56740
+ };
56741
+ DEFAULT_MODELS = {
56742
+ anthropic: "claude-sonnet-4-5-20250929",
56743
+ openai: "gpt-4o",
56744
+ google: "gemini-2.0-flash-exp",
56745
+ bedrock: "anthropic.claude-sonnet-4-20250514-v1:0"
56746
+ };
56747
+ FallbackManager = class {
56748
+ /**
56749
+ * Create a new FallbackManager
56750
+ * @param {Object} options - Configuration options
56751
+ * @param {string} [options.strategy='any'] - Fallback strategy
56752
+ * @param {Array<string>} [options.models] - List of models for same-provider fallback
56753
+ * @param {Array<ProviderConfig>} [options.providers] - List of provider configurations
56754
+ * @param {boolean} [options.stopOnSuccess=true] - Stop on first success
56755
+ * @param {boolean} [options.continueOnNonRetryableError=false] - Continue to fallback on non-retryable errors
56756
+ * @param {number} [options.maxTotalAttempts=10] - Maximum total attempts across all providers
56757
+ * @param {boolean} [options.debug=false] - Enable debug logging
56758
+ */
56759
+ constructor(options = {}) {
56760
+ this.strategy = options.strategy || FALLBACK_STRATEGIES.ANY;
56761
+ this.models = Array.isArray(options.models) ? options.models : [];
56762
+ this.providers = Array.isArray(options.providers) ? options.providers : [];
56763
+ this.stopOnSuccess = options.stopOnSuccess ?? true;
56764
+ this.continueOnNonRetryableError = options.continueOnNonRetryableError ?? false;
56765
+ this.debug = options.debug ?? false;
56766
+ const maxAttempts = options.maxTotalAttempts ?? 10;
56767
+ if (typeof maxAttempts !== "number" || isNaN(maxAttempts) || maxAttempts < 1 || maxAttempts > 100) {
56768
+ throw new Error(`FallbackManager: maxTotalAttempts must be a number between 1 and 100, got: ${maxAttempts}`);
56769
+ }
56770
+ this.maxTotalAttempts = maxAttempts;
56771
+ this.stats = {
56772
+ totalAttempts: 0,
56773
+ providerAttempts: {},
56774
+ successfulProvider: null,
56775
+ failedProviders: []
56776
+ };
56777
+ this._validateConfiguration();
56778
+ }
56779
+ /**
56780
+ * Validate the fallback configuration
56781
+ * @private
56782
+ */
56783
+ _validateConfiguration() {
56784
+ if (this.strategy === FALLBACK_STRATEGIES.SAME_PROVIDER && this.models.length === 0) {
56785
+ throw new Error('FallbackManager: strategy "same-provider" requires models list');
56786
+ }
56787
+ if (this.strategy === FALLBACK_STRATEGIES.CUSTOM && this.providers.length === 0) {
56788
+ throw new Error('FallbackManager: strategy "custom" requires providers list');
56789
+ }
56790
+ for (const config of this.providers) {
56791
+ if (!config.provider) {
56792
+ throw new Error('FallbackManager: Each provider config must have a "provider" field');
56793
+ }
56794
+ if (!["anthropic", "openai", "google", "bedrock"].includes(config.provider)) {
56795
+ throw new Error(`FallbackManager: Invalid provider "${config.provider}". Must be: anthropic, openai, google, or bedrock`);
56796
+ }
56797
+ if (config.provider === "bedrock") {
56798
+ const hasCredentials = config.accessKeyId && config.secretAccessKey && config.region;
56799
+ const hasApiKey = config.apiKey;
56800
+ if (!hasCredentials && !hasApiKey) {
56801
+ throw new Error("FallbackManager: Bedrock provider requires either (accessKeyId, secretAccessKey, region) or apiKey");
56802
+ }
56803
+ } else {
56804
+ if (!config.apiKey) {
56805
+ throw new Error(`FallbackManager: Provider "${config.provider}" requires apiKey`);
56806
+ }
56807
+ }
56808
+ }
56809
+ }
56810
+ /**
56811
+ * Create a provider instance from configuration
56812
+ * @param {ProviderConfig} config - Provider configuration
56813
+ * @returns {Object} - Provider instance
56814
+ * @throws {Error} - If provider creation fails
56815
+ * @private
56816
+ */
56817
+ _createProviderInstance(config) {
56818
+ try {
56819
+ switch (config.provider) {
56820
+ case "anthropic":
56821
+ return createAnthropic({
56822
+ apiKey: config.apiKey,
56823
+ ...config.baseURL && { baseURL: config.baseURL }
56824
+ });
56825
+ case "openai":
56826
+ return createOpenAI({
56827
+ compatibility: "strict",
56828
+ apiKey: config.apiKey,
56829
+ ...config.baseURL && { baseURL: config.baseURL }
56830
+ });
56831
+ case "google":
56832
+ return createGoogleGenerativeAI({
56833
+ apiKey: config.apiKey,
56834
+ ...config.baseURL && { baseURL: config.baseURL }
56835
+ });
56836
+ case "bedrock": {
56837
+ const bedrockConfig = {};
56838
+ if (config.apiKey) {
56839
+ bedrockConfig.apiKey = config.apiKey;
56840
+ } else if (config.accessKeyId && config.secretAccessKey) {
56841
+ bedrockConfig.accessKeyId = config.accessKeyId;
56842
+ bedrockConfig.secretAccessKey = config.secretAccessKey;
56843
+ if (config.sessionToken) {
56844
+ bedrockConfig.sessionToken = config.sessionToken;
56845
+ }
56846
+ }
56847
+ if (config.region) {
56848
+ bedrockConfig.region = config.region;
56849
+ }
56850
+ if (config.baseURL) {
56851
+ bedrockConfig.baseURL = config.baseURL;
56852
+ }
56853
+ return createAmazonBedrock(bedrockConfig);
56854
+ }
56855
+ default:
56856
+ throw new Error(`FallbackManager: Unknown provider "${config.provider}"`);
56857
+ }
56858
+ } catch (error) {
56859
+ const providerName = this._getProviderDisplayName(config);
56860
+ throw new Error(`Failed to create provider instance for ${providerName}: ${error.message}`);
56861
+ }
56862
+ }
56863
+ /**
56864
+ * Get the model name for a provider configuration
56865
+ * @param {ProviderConfig} config - Provider configuration
56866
+ * @returns {string} - Model name
56867
+ * @private
56868
+ */
56869
+ _getModelName(config) {
56870
+ return config.model || DEFAULT_MODELS[config.provider];
56871
+ }
56872
+ /**
56873
+ * Get provider display name for logging
56874
+ * @param {ProviderConfig} config - Provider configuration
56875
+ * @returns {string} - Display name
56876
+ * @private
56877
+ */
56878
+ _getProviderDisplayName(config) {
56879
+ const model = this._getModelName(config);
56880
+ const provider = config.provider;
56881
+ const url = config.baseURL ? ` (${config.baseURL})` : "";
56882
+ return `${provider}/${model}${url}`;
56883
+ }
56884
+ /**
56885
+ * Execute a function with fallback support
56886
+ * @param {Function} fn - Function that takes (provider, model, config) and returns a Promise
56887
+ * @returns {Promise<*>} - Result from the function
56888
+ * @throws {Error} - If all fallbacks are exhausted
56889
+ */
56890
+ async executeWithFallback(fn) {
56891
+ if (this.providers.length === 0) {
56892
+ throw new Error("FallbackManager: No providers configured for fallback");
56893
+ }
56894
+ let lastError = null;
56895
+ let totalAttempts = 0;
56896
+ for (const config of this.providers) {
56897
+ if (totalAttempts >= this.maxTotalAttempts) {
56898
+ if (this.debug) {
56899
+ console.log(`[FallbackManager] \u26A0\uFE0F Max total attempts (${this.maxTotalAttempts}) reached`);
56900
+ }
56901
+ break;
56902
+ }
56903
+ totalAttempts++;
56904
+ this.stats.totalAttempts++;
56905
+ const providerName = this._getProviderDisplayName(config);
56906
+ this.stats.providerAttempts[providerName] = (this.stats.providerAttempts[providerName] || 0) + 1;
56907
+ try {
56908
+ if (this.debug) {
56909
+ console.log(`[FallbackManager] Attempting provider: ${providerName} (attempt ${totalAttempts}/${this.maxTotalAttempts})`);
56910
+ }
56911
+ const provider = this._createProviderInstance(config);
56912
+ const model = this._getModelName(config);
56913
+ const result = await fn(provider, model, config);
56914
+ this.stats.successfulProvider = providerName;
56915
+ if (this.debug) {
56916
+ console.log(`[FallbackManager] \u2705 Success with provider: ${providerName}`);
56917
+ }
56918
+ return result;
56919
+ } catch (error) {
56920
+ lastError = error;
56921
+ const errorInfo = {
56922
+ message: error.message || error.toString(),
56923
+ type: error.type || error.constructor.name,
56924
+ statusCode: error.statusCode || error.status
56925
+ };
56926
+ this.stats.failedProviders.push({
56927
+ provider: providerName,
56928
+ error: errorInfo
56929
+ });
56930
+ if (this.debug) {
56931
+ console.log(`[FallbackManager] \u274C Failed with provider: ${providerName}`, errorInfo);
56932
+ }
56933
+ if (!this.continueOnNonRetryableError && error.nonRetryable) {
56934
+ if (this.debug) {
56935
+ console.log(`[FallbackManager] Non-retryable error, stopping fallback chain`);
56936
+ }
56937
+ throw error;
56938
+ }
56939
+ if (this.debug) {
56940
+ const remaining = this.providers.length - (this.providers.indexOf(config) + 1);
56941
+ console.log(`[FallbackManager] Trying next provider (${remaining} remaining)...`);
56942
+ }
56943
+ }
56944
+ }
56945
+ if (this.debug) {
56946
+ console.log(`[FallbackManager] \u274C All providers exhausted. Total attempts: ${totalAttempts}`);
56947
+ }
56948
+ const fallbackError = new Error(
56949
+ `All provider fallbacks exhausted after ${totalAttempts} attempts. Last error: ${lastError?.message || "Unknown error"}`
56950
+ );
56951
+ fallbackError.cause = lastError;
56952
+ fallbackError.stats = this.getStats();
56953
+ fallbackError.allProvidersFailed = true;
56954
+ throw fallbackError;
56955
+ }
56956
+ /**
56957
+ * Get fallback statistics
56958
+ * @returns {Object} - Statistics object
56959
+ */
56960
+ getStats() {
56961
+ return {
56962
+ ...this.stats,
56963
+ providerAttempts: { ...this.stats.providerAttempts },
56964
+ failedProviders: [...this.stats.failedProviders]
56965
+ };
56966
+ }
56967
+ /**
56968
+ * Reset statistics
56969
+ */
56970
+ resetStats() {
56971
+ this.stats = {
56972
+ totalAttempts: 0,
56973
+ providerAttempts: {},
56974
+ successfulProvider: null,
56975
+ failedProviders: []
56976
+ };
56977
+ }
56978
+ };
56979
+ }
56980
+ });
56981
+
56982
+ // src/agent/contextCompactor.js
56983
+ var contextCompactor_exports = {};
56984
+ __export(contextCompactor_exports, {
56985
+ calculateCompactionStats: () => calculateCompactionStats,
56986
+ compactMessages: () => compactMessages,
56987
+ handleContextLimitError: () => handleContextLimitError,
56988
+ identifyMessageSegments: () => identifyMessageSegments,
56989
+ isContextLimitError: () => isContextLimitError
56990
+ });
56991
+ function isContextLimitError(error) {
56992
+ if (!error) return false;
56993
+ const errorMessage = (typeof error === "string" ? error : error?.message || "").toLowerCase();
56994
+ const errorString = error.toString().toLowerCase();
56995
+ for (const pattern of CONTEXT_LIMIT_ERROR_PATTERNS) {
56996
+ const lowerPattern = pattern.toLowerCase();
56997
+ if (errorMessage.includes(lowerPattern) || errorString.includes(lowerPattern)) {
56998
+ return true;
56999
+ }
57000
+ }
57001
+ return false;
57002
+ }
57003
+ function identifyMessageSegments(messages) {
57004
+ const segments = [];
57005
+ let currentSegment = null;
57006
+ for (let i = 0; i < messages.length; i++) {
57007
+ const msg = messages[i];
57008
+ if (msg.role === "system") {
57009
+ continue;
57010
+ }
57011
+ if (msg.role === "user") {
57012
+ const content = typeof msg.content === "string" ? msg.content : "";
57013
+ const isToolResult = content.includes("<tool_result>");
57014
+ if (isToolResult && currentSegment) {
57015
+ currentSegment.finalIndex = i;
57016
+ segments.push(currentSegment);
57017
+ currentSegment = null;
57018
+ } else {
57019
+ if (currentSegment) {
57020
+ segments.push(currentSegment);
57021
+ }
57022
+ currentSegment = {
57023
+ userIndex: i,
57024
+ monologueIndices: [],
57025
+ finalIndex: null
57026
+ };
57027
+ }
57028
+ }
57029
+ if (msg.role === "assistant" && currentSegment) {
57030
+ const content = typeof msg.content === "string" ? msg.content : "";
57031
+ if (content.includes("<attempt_completion>") || content.includes("attempt_completion")) {
57032
+ currentSegment.monologueIndices.push(i);
57033
+ currentSegment.finalIndex = i;
57034
+ segments.push(currentSegment);
57035
+ currentSegment = null;
57036
+ } else {
57037
+ currentSegment.monologueIndices.push(i);
57038
+ }
57039
+ }
57040
+ }
57041
+ if (currentSegment) {
57042
+ segments.push(currentSegment);
57043
+ }
57044
+ return segments;
57045
+ }
57046
+ function compactMessages(messages, options = {}) {
57047
+ const {
57048
+ keepLastSegment = true,
57049
+ minSegmentsToKeep = 1
57050
+ } = options;
57051
+ if (!messages || messages.length === 0) {
57052
+ return messages;
57053
+ }
57054
+ const segments = identifyMessageSegments(messages);
57055
+ if (segments.length === 0) {
57056
+ return messages;
57057
+ }
57058
+ const segmentsToPreserve = keepLastSegment ? Math.max(minSegmentsToKeep, 1) : minSegmentsToKeep;
57059
+ const compactableSegments = segments.slice(0, -segmentsToPreserve);
57060
+ const preservedSegments = segments.slice(-segmentsToPreserve);
57061
+ const indicesToKeep = /* @__PURE__ */ new Set();
57062
+ messages.forEach((msg, idx) => {
57063
+ if (msg.role === "system") {
57064
+ indicesToKeep.add(idx);
57065
+ }
57066
+ });
57067
+ compactableSegments.forEach((segment) => {
57068
+ indicesToKeep.add(segment.userIndex);
57069
+ if (segment.finalIndex !== null) {
57070
+ indicesToKeep.add(segment.finalIndex);
57071
+ }
57072
+ });
57073
+ preservedSegments.forEach((segment) => {
57074
+ indicesToKeep.add(segment.userIndex);
57075
+ segment.monologueIndices.forEach((idx) => indicesToKeep.add(idx));
57076
+ if (segment.finalIndex !== null) {
57077
+ indicesToKeep.add(segment.finalIndex);
57078
+ }
57079
+ });
57080
+ const compactedMessages = messages.filter((_, idx) => indicesToKeep.has(idx));
57081
+ return compactedMessages;
57082
+ }
57083
+ function calculateCompactionStats(originalMessages, compactedMessages) {
57084
+ const originalCount = originalMessages.length;
57085
+ const compactedCount = compactedMessages.length;
57086
+ const removed = originalCount - compactedCount;
57087
+ const reductionPercent = originalCount > 0 ? (removed / originalCount * 100).toFixed(1) : 0;
57088
+ const estimateTokens = (msgs) => {
57089
+ return msgs.reduce((sum, msg) => {
57090
+ const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
57091
+ return sum + Math.ceil(content.length / 4);
57092
+ }, 0);
57093
+ };
57094
+ const originalTokens = estimateTokens(originalMessages);
57095
+ const compactedTokens = estimateTokens(compactedMessages);
57096
+ const tokensSaved = originalTokens - compactedTokens;
57097
+ return {
57098
+ originalCount,
57099
+ compactedCount,
57100
+ removed,
57101
+ reductionPercent: parseFloat(reductionPercent),
57102
+ originalTokens,
57103
+ compactedTokens,
57104
+ tokensSaved
57105
+ };
57106
+ }
57107
+ function handleContextLimitError(error, messages, options = {}) {
57108
+ if (!isContextLimitError(error)) {
57109
+ return null;
57110
+ }
57111
+ const compactedMessages = compactMessages(messages, options);
57112
+ const stats = calculateCompactionStats(messages, compactedMessages);
57113
+ return {
57114
+ compacted: true,
57115
+ messages: compactedMessages,
57116
+ stats
57117
+ };
57118
+ }
57119
+ var CONTEXT_LIMIT_ERROR_PATTERNS;
57120
+ var init_contextCompactor = __esm({
57121
+ "src/agent/contextCompactor.js"() {
57122
+ "use strict";
57123
+ CONTEXT_LIMIT_ERROR_PATTERNS = [
57124
+ // Anthropic
57125
+ "context_length_exceeded",
57126
+ "prompt is too long",
57127
+ // OpenAI
57128
+ "maximum context length",
57129
+ "context length is",
57130
+ // Google/Gemini
57131
+ "input token count exceeds",
57132
+ "token limit exceeded",
57133
+ // Generic patterns
57134
+ "context window",
57135
+ "too many tokens",
57136
+ "token limit",
57137
+ "context limit",
57138
+ "exceed",
57139
+ // Catches "exceeds", "exceed maximum", etc.
57140
+ "over the limit",
57141
+ "maximum tokens"
57142
+ ];
57143
+ }
57144
+ });
57145
+
55612
57146
  // src/agent/ProbeAgent.js
55613
57147
  var ProbeAgent_exports = {};
55614
57148
  __export(ProbeAgent_exports, {
55615
57149
  ProbeAgent: () => ProbeAgent
55616
57150
  });
55617
57151
  import dotenv2 from "dotenv";
55618
- import { createAnthropic } from "@ai-sdk/anthropic";
55619
- import { createOpenAI } from "@ai-sdk/openai";
55620
- import { createGoogleGenerativeAI } from "@ai-sdk/google";
55621
- import { createAmazonBedrock } from "@ai-sdk/amazon-bedrock";
57152
+ import { createAnthropic as createAnthropic2 } from "@ai-sdk/anthropic";
57153
+ import { createOpenAI as createOpenAI2 } from "@ai-sdk/openai";
57154
+ import { createGoogleGenerativeAI as createGoogleGenerativeAI2 } from "@ai-sdk/google";
57155
+ import { createAmazonBedrock as createAmazonBedrock2 } from "@ai-sdk/amazon-bedrock";
55622
57156
  import { streamText } from "ai";
55623
57157
  import { randomUUID as randomUUID4 } from "crypto";
55624
57158
  import { EventEmitter as EventEmitter3 } from "events";
55625
- import { existsSync as existsSync4 } from "fs";
57159
+ import { existsSync as existsSync5 } from "fs";
55626
57160
  import { readFile, stat } from "fs/promises";
55627
- import { resolve as resolve3, isAbsolute, dirname as dirname3 } from "path";
57161
+ import { resolve as resolve4, isAbsolute as isAbsolute2, dirname as dirname4 } from "path";
55628
57162
  var MAX_TOOL_ITERATIONS, MAX_HISTORY_MESSAGES, MAX_IMAGE_FILE_SIZE, ProbeAgent;
55629
57163
  var init_ProbeAgent = __esm({
55630
57164
  "src/agent/ProbeAgent.js"() {
@@ -55641,8 +57175,18 @@ var init_ProbeAgent = __esm({
55641
57175
  init_schemaUtils();
55642
57176
  init_xmlParsingUtils();
55643
57177
  init_mcp();
57178
+ init_RetryManager();
57179
+ init_FallbackManager();
57180
+ init_contextCompactor();
55644
57181
  dotenv2.config();
55645
- MAX_TOOL_ITERATIONS = parseInt(process.env.MAX_TOOL_ITERATIONS || "30", 10);
57182
+ MAX_TOOL_ITERATIONS = (() => {
57183
+ const val = parseInt(process.env.MAX_TOOL_ITERATIONS || "30", 10);
57184
+ if (isNaN(val) || val < 1 || val > 200) {
57185
+ console.warn("[ProbeAgent] MAX_TOOL_ITERATIONS must be between 1 and 200, using default: 30");
57186
+ return 30;
57187
+ }
57188
+ return val;
57189
+ })();
55646
57190
  MAX_HISTORY_MESSAGES = 100;
55647
57191
  MAX_IMAGE_FILE_SIZE = 20 * 1024 * 1024;
55648
57192
  ProbeAgent = class _ProbeAgent {
@@ -55669,6 +57213,20 @@ var init_ProbeAgent = __esm({
55669
57213
  * @param {Array} [options.mcpServers] - Deprecated, use mcpConfig instead
55670
57214
  * @param {Object} [options.storageAdapter] - Custom storage adapter for history management
55671
57215
  * @param {Object} [options.hooks] - Hook callbacks for events (e.g., {'tool:start': callback})
57216
+ * @param {Array<string>|null} [options.allowedTools] - List of allowed tool names. Use ['*'] for all tools (default), [] or null for no tools (raw AI mode), or specific tool names like ['search', 'query', 'extract']. Supports exclusion with '!' prefix (e.g., ['*', '!bash'])
57217
+ * @param {boolean} [options.disableTools=false] - Convenience flag to disable all tools (equivalent to allowedTools: []). Takes precedence over allowedTools if set.
57218
+ * @param {Object} [options.retry] - Retry configuration
57219
+ * @param {number} [options.retry.maxRetries=3] - Maximum retry attempts per provider
57220
+ * @param {number} [options.retry.initialDelay=1000] - Initial delay in ms
57221
+ * @param {number} [options.retry.maxDelay=30000] - Maximum delay in ms
57222
+ * @param {number} [options.retry.backoffFactor=2] - Exponential backoff multiplier
57223
+ * @param {Array<string>} [options.retry.retryableErrors] - List of retryable error patterns
57224
+ * @param {Object} [options.fallback] - Fallback configuration
57225
+ * @param {string} [options.fallback.strategy] - Fallback strategy: 'same-model', 'same-provider', 'any', 'custom'
57226
+ * @param {Array<string>} [options.fallback.models] - List of models for same-provider fallback
57227
+ * @param {Array<Object>} [options.fallback.providers] - List of provider configurations for custom fallback
57228
+ * @param {boolean} [options.fallback.stopOnSuccess=true] - Stop on first success
57229
+ * @param {number} [options.fallback.maxTotalAttempts=10] - Maximum total attempts across all providers
55672
57230
  */
55673
57231
  constructor(options = {}) {
55674
57232
  this.sessionId = options.sessionId || randomUUID4();
@@ -55680,10 +57238,18 @@ var init_ProbeAgent = __esm({
55680
57238
  this.cancelled = false;
55681
57239
  this.tracer = options.tracer || null;
55682
57240
  this.outline = !!options.outline;
55683
- this.maxResponseTokens = options.maxResponseTokens || parseInt(process.env.MAX_RESPONSE_TOKENS || "0", 10) || null;
57241
+ this.maxResponseTokens = options.maxResponseTokens || (() => {
57242
+ const val = parseInt(process.env.MAX_RESPONSE_TOKENS || "0", 10);
57243
+ if (isNaN(val) || val < 0 || val > 2e5) {
57244
+ return null;
57245
+ }
57246
+ return val || null;
57247
+ })();
55684
57248
  this.maxIterations = options.maxIterations || null;
55685
57249
  this.disableMermaidValidation = !!options.disableMermaidValidation;
55686
57250
  this.disableJsonValidation = !!options.disableJsonValidation;
57251
+ const effectiveAllowedTools = options.disableTools ? [] : options.allowedTools;
57252
+ this.allowedTools = this._parseAllowedTools(effectiveAllowedTools);
55687
57253
  this.storageAdapter = options.storageAdapter || new InMemoryStorageAdapter();
55688
57254
  this.hooks = new HookManager();
55689
57255
  if (options.hooks) {
@@ -55721,8 +57287,67 @@ var init_ProbeAgent = __esm({
55721
57287
  this.mcpServers = options.mcpServers || null;
55722
57288
  this.mcpBridge = null;
55723
57289
  this._mcpInitialized = false;
57290
+ this.retryConfig = options.retry || {};
57291
+ this.retryManager = null;
57292
+ this.fallbackConfig = options.fallback || null;
57293
+ this.fallbackManager = null;
55724
57294
  this.initializeModel();
55725
57295
  }
57296
+ /**
57297
+ * Parse allowedTools configuration
57298
+ * @param {Array<string>|null|undefined} allowedTools - Tool filtering configuration
57299
+ * @returns {Object} Parsed configuration with isEnabled method
57300
+ * @private
57301
+ */
57302
+ _parseAllowedTools(allowedTools) {
57303
+ const matchesPattern2 = (toolName, pattern) => {
57304
+ if (!pattern.includes("*")) {
57305
+ return toolName === pattern;
57306
+ }
57307
+ const regexPattern = pattern.replace(/\*/g, ".*");
57308
+ return new RegExp(`^${regexPattern}$`).test(toolName);
57309
+ };
57310
+ if (!allowedTools || Array.isArray(allowedTools) && allowedTools.includes("*")) {
57311
+ const exclusions = Array.isArray(allowedTools) ? allowedTools.filter((t) => t.startsWith("!")).map((t) => t.slice(1)) : [];
57312
+ return {
57313
+ mode: "all",
57314
+ exclusions,
57315
+ isEnabled: (toolName) => !exclusions.some((pattern) => matchesPattern2(toolName, pattern))
57316
+ };
57317
+ }
57318
+ if (Array.isArray(allowedTools) && allowedTools.length === 0) {
57319
+ return {
57320
+ mode: "none",
57321
+ isEnabled: () => false
57322
+ };
57323
+ }
57324
+ const allowedPatterns = allowedTools.filter((t) => !t.startsWith("!"));
57325
+ return {
57326
+ mode: "whitelist",
57327
+ allowed: allowedPatterns,
57328
+ isEnabled: (toolName) => allowedPatterns.some((pattern) => matchesPattern2(toolName, pattern))
57329
+ };
57330
+ }
57331
+ /**
57332
+ * Check if an MCP tool is allowed based on allowedTools configuration
57333
+ * Uses mcp__ prefix convention (like Claude Code)
57334
+ * @param {string} toolName - The MCP tool name (without mcp__ prefix)
57335
+ * @returns {boolean} - Whether the tool is allowed
57336
+ * @private
57337
+ */
57338
+ _isMcpToolAllowed(toolName) {
57339
+ const mcpToolName = `mcp__${toolName}`;
57340
+ return this.allowedTools.isEnabled(mcpToolName) || this.allowedTools.isEnabled(toolName);
57341
+ }
57342
+ /**
57343
+ * Filter MCP tools based on allowedTools configuration
57344
+ * @param {string[]} mcpToolNames - Array of MCP tool names
57345
+ * @returns {string[]} - Filtered array of allowed MCP tool names
57346
+ * @private
57347
+ */
57348
+ _filterMcpTools(mcpToolNames) {
57349
+ return mcpToolNames.filter((toolName) => this._isMcpToolAllowed(toolName));
57350
+ }
55726
57351
  /**
55727
57352
  * Initialize the agent asynchronously (must be called after constructor)
55728
57353
  * This method initializes MCP and merges MCP tools into the tool list, and loads history from storage
@@ -55749,7 +57374,11 @@ var init_ProbeAgent = __esm({
55749
57374
  if (this.mcpBridge) {
55750
57375
  const mcpTools = this.mcpBridge.mcpTools || {};
55751
57376
  for (const [toolName, toolImpl] of Object.entries(mcpTools)) {
55752
- this.toolImplementations[toolName] = toolImpl;
57377
+ if (this._isMcpToolAllowed(toolName)) {
57378
+ this.toolImplementations[toolName] = toolImpl;
57379
+ } else if (this.debug) {
57380
+ console.error(`[DEBUG] MCP tool '${toolName}' filtered out by allowedTools`);
57381
+ }
55753
57382
  }
55754
57383
  }
55755
57384
  if (this.debug) {
@@ -55805,6 +57434,14 @@ var init_ProbeAgent = __esm({
55805
57434
  if (this.enableBash && wrappedTools.bashToolInstance) {
55806
57435
  this.toolImplementations.bash = wrappedTools.bashToolInstance;
55807
57436
  }
57437
+ if (this.allowEdit) {
57438
+ if (wrappedTools.editToolInstance) {
57439
+ this.toolImplementations.edit = wrappedTools.editToolInstance;
57440
+ }
57441
+ if (wrappedTools.createToolInstance) {
57442
+ this.toolImplementations.create = wrappedTools.createToolInstance;
57443
+ }
57444
+ }
55808
57445
  this.wrappedTools = wrappedTools;
55809
57446
  if (this.debug) {
55810
57447
  console.error("\n[DEBUG] ========================================");
@@ -55855,36 +57492,147 @@ var init_ProbeAgent = __esm({
55855
57492
  if (forceProvider) {
55856
57493
  if (forceProvider === "anthropic" && anthropicApiKey) {
55857
57494
  this.initializeAnthropicModel(anthropicApiKey, anthropicApiUrl, modelName);
57495
+ this.initializeFallbackManager(forceProvider, modelName);
55858
57496
  return;
55859
57497
  } else if (forceProvider === "openai" && openaiApiKey) {
55860
57498
  this.initializeOpenAIModel(openaiApiKey, openaiApiUrl, modelName);
57499
+ this.initializeFallbackManager(forceProvider, modelName);
55861
57500
  return;
55862
57501
  } else if (forceProvider === "google" && googleApiKey) {
55863
57502
  this.initializeGoogleModel(googleApiKey, googleApiUrl, modelName);
57503
+ this.initializeFallbackManager(forceProvider, modelName);
55864
57504
  return;
55865
57505
  } else if (forceProvider === "bedrock" && (awsAccessKeyId && awsSecretAccessKey && awsRegion || awsApiKey)) {
55866
57506
  this.initializeBedrockModel(awsAccessKeyId, awsSecretAccessKey, awsRegion, awsSessionToken, awsApiKey, awsBedrockBaseUrl, modelName);
57507
+ this.initializeFallbackManager(forceProvider, modelName);
55867
57508
  return;
55868
57509
  }
55869
57510
  console.warn(`WARNING: Forced provider "${forceProvider}" selected but required API key is missing or invalid! Falling back to auto-detection.`);
55870
57511
  }
55871
57512
  if (anthropicApiKey) {
55872
57513
  this.initializeAnthropicModel(anthropicApiKey, anthropicApiUrl, modelName);
57514
+ this.initializeFallbackManager("anthropic", modelName);
55873
57515
  } else if (openaiApiKey) {
55874
57516
  this.initializeOpenAIModel(openaiApiKey, openaiApiUrl, modelName);
57517
+ this.initializeFallbackManager("openai", modelName);
55875
57518
  } else if (googleApiKey) {
55876
57519
  this.initializeGoogleModel(googleApiKey, googleApiUrl, modelName);
57520
+ this.initializeFallbackManager("google", modelName);
55877
57521
  } else if (awsAccessKeyId && awsSecretAccessKey && awsRegion || awsApiKey) {
55878
57522
  this.initializeBedrockModel(awsAccessKeyId, awsSecretAccessKey, awsRegion, awsSessionToken, awsApiKey, awsBedrockBaseUrl, modelName);
57523
+ this.initializeFallbackManager("bedrock", modelName);
55879
57524
  } else {
55880
57525
  throw new Error("No API key provided. Please set ANTHROPIC_API_KEY (or ANTHROPIC_AUTH_TOKEN), OPENAI_API_KEY, GOOGLE_GENERATIVE_AI_API_KEY (or GOOGLE_API_KEY), AWS credentials (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION), or AWS_BEDROCK_API_KEY environment variables.");
55881
57526
  }
55882
57527
  }
57528
+ /**
57529
+ * Initialize fallback manager based on configuration
57530
+ * @param {string} primaryProvider - The primary provider being used
57531
+ * @param {string} primaryModel - The primary model being used
57532
+ * @private
57533
+ */
57534
+ initializeFallbackManager(primaryProvider, primaryModel) {
57535
+ if (this.fallbackConfig === false || process.env.DISABLE_FALLBACK === "1") {
57536
+ return;
57537
+ }
57538
+ if (this.fallbackConfig && this.fallbackConfig.providers) {
57539
+ try {
57540
+ this.fallbackManager = new FallbackManager({
57541
+ ...this.fallbackConfig,
57542
+ debug: this.debug
57543
+ });
57544
+ if (this.debug) {
57545
+ console.log(`[DEBUG] Fallback manager initialized with ${this.fallbackManager.providers.length} providers`);
57546
+ }
57547
+ } catch (error) {
57548
+ console.error("[WARNING] Failed to initialize fallback manager:", error.message);
57549
+ }
57550
+ return;
57551
+ }
57552
+ const envFallbackManager = createFallbackManagerFromEnv(this.debug);
57553
+ if (envFallbackManager) {
57554
+ this.fallbackManager = envFallbackManager;
57555
+ if (this.debug) {
57556
+ console.log(`[DEBUG] Fallback manager initialized from environment variables`);
57557
+ }
57558
+ return;
57559
+ }
57560
+ if (process.env.AUTO_FALLBACK === "1" || this.fallbackConfig?.auto) {
57561
+ const providers = buildFallbackProvidersFromEnv({
57562
+ primaryProvider,
57563
+ primaryModel
57564
+ });
57565
+ if (providers.length > 1) {
57566
+ try {
57567
+ this.fallbackManager = new FallbackManager({
57568
+ strategy: "custom",
57569
+ providers,
57570
+ debug: this.debug
57571
+ });
57572
+ if (this.debug) {
57573
+ console.log(`[DEBUG] Auto-fallback enabled with ${providers.length} providers`);
57574
+ }
57575
+ } catch (error) {
57576
+ console.error("[WARNING] Failed to initialize auto-fallback:", error.message);
57577
+ }
57578
+ }
57579
+ }
57580
+ }
57581
+ /**
57582
+ * Execute streamText with retry and fallback support
57583
+ * @param {Object} options - streamText options
57584
+ * @returns {Promise<Object>} - streamText result
57585
+ * @private
57586
+ */
57587
+ async streamTextWithRetryAndFallback(options) {
57588
+ if (!this.retryManager) {
57589
+ this.retryManager = new RetryManager({
57590
+ maxRetries: this.retryConfig.maxRetries ?? 3,
57591
+ initialDelay: this.retryConfig.initialDelay ?? 1e3,
57592
+ maxDelay: this.retryConfig.maxDelay ?? 3e4,
57593
+ backoffFactor: this.retryConfig.backoffFactor ?? 2,
57594
+ retryableErrors: this.retryConfig.retryableErrors,
57595
+ debug: this.debug
57596
+ });
57597
+ }
57598
+ if (!this.fallbackManager) {
57599
+ return await this.retryManager.executeWithRetry(
57600
+ () => streamText(options),
57601
+ {
57602
+ provider: this.apiType,
57603
+ model: this.model
57604
+ }
57605
+ );
57606
+ }
57607
+ return await this.fallbackManager.executeWithFallback(
57608
+ async (provider, model, config) => {
57609
+ const fallbackOptions = {
57610
+ ...options,
57611
+ model: provider(model)
57612
+ };
57613
+ const providerRetryManager = new RetryManager({
57614
+ maxRetries: config.maxRetries ?? this.retryConfig.maxRetries ?? 3,
57615
+ initialDelay: this.retryConfig.initialDelay ?? 1e3,
57616
+ maxDelay: this.retryConfig.maxDelay ?? 3e4,
57617
+ backoffFactor: this.retryConfig.backoffFactor ?? 2,
57618
+ retryableErrors: this.retryConfig.retryableErrors,
57619
+ debug: this.debug
57620
+ });
57621
+ return await providerRetryManager.executeWithRetry(
57622
+ () => streamText(fallbackOptions),
57623
+ {
57624
+ provider: config.provider,
57625
+ model
57626
+ }
57627
+ );
57628
+ }
57629
+ );
57630
+ }
55883
57631
  /**
55884
57632
  * Initialize Anthropic model
55885
57633
  */
55886
57634
  initializeAnthropicModel(apiKey, apiUrl, modelName) {
55887
- this.provider = createAnthropic({
57635
+ this.provider = createAnthropic2({
55888
57636
  apiKey,
55889
57637
  ...apiUrl && { baseURL: apiUrl }
55890
57638
  });
@@ -55898,7 +57646,7 @@ var init_ProbeAgent = __esm({
55898
57646
  * Initialize OpenAI model
55899
57647
  */
55900
57648
  initializeOpenAIModel(apiKey, apiUrl, modelName) {
55901
- this.provider = createOpenAI({
57649
+ this.provider = createOpenAI2({
55902
57650
  compatibility: "strict",
55903
57651
  apiKey,
55904
57652
  ...apiUrl && { baseURL: apiUrl }
@@ -55913,7 +57661,7 @@ var init_ProbeAgent = __esm({
55913
57661
  * Initialize Google model
55914
57662
  */
55915
57663
  initializeGoogleModel(apiKey, apiUrl, modelName) {
55916
- this.provider = createGoogleGenerativeAI({
57664
+ this.provider = createGoogleGenerativeAI2({
55917
57665
  apiKey,
55918
57666
  ...apiUrl && { baseURL: apiUrl }
55919
57667
  });
@@ -55943,7 +57691,7 @@ var init_ProbeAgent = __esm({
55943
57691
  if (baseURL) {
55944
57692
  config.baseURL = baseURL;
55945
57693
  }
55946
- this.provider = createAmazonBedrock(config);
57694
+ this.provider = createAmazonBedrock2(config);
55947
57695
  this.model = modelName || "anthropic.claude-sonnet-4-20250514-v1:0";
55948
57696
  this.apiType = "bedrock";
55949
57697
  if (this.debug) {
@@ -55988,7 +57736,7 @@ var init_ProbeAgent = __esm({
55988
57736
  let resolvedPath = imagePath;
55989
57737
  if (!imagePath.includes("/") && !imagePath.includes("\\")) {
55990
57738
  for (const dir of listFilesDirectories) {
55991
- const potentialPath = resolve3(dir, imagePath);
57739
+ const potentialPath = resolve4(dir, imagePath);
55992
57740
  const loaded = await this.loadImageIfValid(potentialPath);
55993
57741
  if (loaded) {
55994
57742
  if (this.debug) {
@@ -56013,7 +57761,7 @@ var init_ProbeAgent = __esm({
56013
57761
  let match2;
56014
57762
  while ((match2 = fileHeaderPattern.exec(content)) !== null) {
56015
57763
  const filePath = match2[1].trim();
56016
- const dir = dirname3(filePath);
57764
+ const dir = dirname4(filePath);
56017
57765
  if (dir && dir !== ".") {
56018
57766
  directories.push(dir);
56019
57767
  if (this.debug) {
@@ -56057,21 +57805,21 @@ var init_ProbeAgent = __esm({
56057
57805
  }
56058
57806
  const allowedDirs = this.allowedFolders && this.allowedFolders.length > 0 ? this.allowedFolders : [process.cwd()];
56059
57807
  let absolutePath;
56060
- let isPathAllowed = false;
56061
- if (isAbsolute(imagePath)) {
57808
+ let isPathAllowed2 = false;
57809
+ if (isAbsolute2(imagePath)) {
56062
57810
  absolutePath = imagePath;
56063
- isPathAllowed = allowedDirs.some((dir) => absolutePath.startsWith(resolve3(dir)));
57811
+ isPathAllowed2 = allowedDirs.some((dir) => absolutePath.startsWith(resolve4(dir)));
56064
57812
  } else {
56065
57813
  for (const dir of allowedDirs) {
56066
- const resolvedPath = resolve3(dir, imagePath);
56067
- if (resolvedPath.startsWith(resolve3(dir))) {
57814
+ const resolvedPath = resolve4(dir, imagePath);
57815
+ if (resolvedPath.startsWith(resolve4(dir))) {
56068
57816
  absolutePath = resolvedPath;
56069
- isPathAllowed = true;
57817
+ isPathAllowed2 = true;
56070
57818
  break;
56071
57819
  }
56072
57820
  }
56073
57821
  }
56074
- if (!isPathAllowed) {
57822
+ if (!isPathAllowed2) {
56075
57823
  if (this.debug) {
56076
57824
  console.log(`[DEBUG] Image path outside allowed directories: ${imagePath}`);
56077
57825
  }
@@ -56250,7 +57998,11 @@ var init_ProbeAgent = __esm({
56250
57998
  if (this.mcpBridge) {
56251
57999
  const mcpTools = this.mcpBridge.mcpTools || {};
56252
58000
  for (const [toolName, toolImpl] of Object.entries(mcpTools)) {
56253
- this.toolImplementations[toolName] = toolImpl;
58001
+ if (this._isMcpToolAllowed(toolName)) {
58002
+ this.toolImplementations[toolName] = toolImpl;
58003
+ } else if (this.debug) {
58004
+ console.error(`[DEBUG] MCP tool '${toolName}' filtered out by allowedTools`);
58005
+ }
56254
58006
  }
56255
58007
  }
56256
58008
  } catch (error) {
@@ -56260,19 +58012,49 @@ var init_ProbeAgent = __esm({
56260
58012
  }
56261
58013
  }
56262
58014
  }
56263
- let toolDefinitions = `
56264
- ${searchToolDefinition}
56265
- ${queryToolDefinition}
56266
- ${extractToolDefinition}
56267
- ${listFilesToolDefinition}
56268
- ${searchFilesToolDefinition}
56269
- ${attemptCompletionToolDefinition}
58015
+ let toolDefinitions = "";
58016
+ const isToolAllowed = (toolName) => this.allowedTools.isEnabled(toolName);
58017
+ if (isToolAllowed("search")) {
58018
+ toolDefinitions += `${searchToolDefinition}
56270
58019
  `;
56271
- if (this.allowEdit) {
58020
+ }
58021
+ if (isToolAllowed("query")) {
58022
+ toolDefinitions += `${queryToolDefinition}
58023
+ `;
58024
+ }
58025
+ if (isToolAllowed("extract")) {
58026
+ toolDefinitions += `${extractToolDefinition}
58027
+ `;
58028
+ }
58029
+ if (isToolAllowed("listFiles")) {
58030
+ toolDefinitions += `${listFilesToolDefinition}
58031
+ `;
58032
+ }
58033
+ if (isToolAllowed("searchFiles")) {
58034
+ toolDefinitions += `${searchFilesToolDefinition}
58035
+ `;
58036
+ }
58037
+ if (this.allowEdit && isToolAllowed("implement")) {
56272
58038
  toolDefinitions += `${implementToolDefinition}
56273
58039
  `;
56274
58040
  }
56275
- if (this.enableDelegate) {
58041
+ if (this.allowEdit && isToolAllowed("edit")) {
58042
+ toolDefinitions += `${editToolDefinition}
58043
+ `;
58044
+ }
58045
+ if (this.allowEdit && isToolAllowed("create")) {
58046
+ toolDefinitions += `${createToolDefinition}
58047
+ `;
58048
+ }
58049
+ if (this.enableBash && isToolAllowed("bash")) {
58050
+ toolDefinitions += `${bashToolDefinition}
58051
+ `;
58052
+ }
58053
+ if (isToolAllowed("attempt_completion")) {
58054
+ toolDefinitions += `${attemptCompletionToolDefinition}
58055
+ `;
58056
+ }
58057
+ if (this.enableDelegate && isToolAllowed("delegate")) {
56276
58058
  toolDefinitions += `${delegateToolDefinition}
56277
58059
  `;
56278
58060
  }
@@ -56337,7 +58119,7 @@ Available Tools:
56337
58119
  - extract: Extract specific code blocks or lines from files.
56338
58120
  - listFiles: List files and directories in a specified location.
56339
58121
  - searchFiles: Find files matching a glob pattern with recursive search capability.
56340
- ${this.allowEdit ? "- implement: Implement a feature or fix a bug using aider.\n" : ""}${this.enableDelegate ? "- delegate: Delegate big distinct tasks to specialized probe subagents.\n" : ""}
58122
+ ${this.allowEdit ? "- implement: Implement a feature or fix a bug using aider.\n- edit: Edit files using exact string replacement.\n- create: Create new files with specified content.\n" : ""}${this.enableDelegate ? "- delegate: Delegate big distinct tasks to specialized probe subagents.\n" : ""}${this.enableBash ? "- bash: Execute bash commands for system operations.\n" : ""}
56341
58123
  - attempt_completion: Finalize the task and provide the result to the user.
56342
58124
  - attempt_complete: Quick completion using previous response (shorthand).
56343
58125
  `;
@@ -56351,7 +58133,10 @@ Follow these instructions carefully:
56351
58133
  6. You MUST respond with exactly ONE tool call per message, using the specified XML format, until the task is complete.
56352
58134
  7. Wait for the tool execution result (provided in the next user message in a <tool_result> block) before proceeding to the next step.
56353
58135
  8. Once the task is fully completed, use the '<attempt_completion>' tool to provide the final result. This is the ONLY way to signal completion.
56354
- 9. Prefer concise and focused search queries. Use specific keywords and phrases to narrow down results.
58136
+ 9. Prefer concise and focused search queries. Use specific keywords and phrases to narrow down results.${this.allowEdit ? `
58137
+ 10. When modifying files, choose the appropriate tool:
58138
+ - Use 'edit' for precise changes to existing files (requires exact string match)
58139
+ - Use 'create' for new files or complete file rewrites` : ""}
56355
58140
  </instructions>
56356
58141
  `;
56357
58142
  const predefinedPrompts = {
@@ -56431,11 +58216,14 @@ ${xmlToolGuidelines}
56431
58216
  ${toolDefinitions}
56432
58217
  `;
56433
58218
  if (this.mcpBridge && this.mcpBridge.getToolNames().length > 0) {
56434
- systemMessage += `
58219
+ const allMcpTools = this.mcpBridge.getToolNames();
58220
+ const allowedMcpTools = this._filterMcpTools(allMcpTools);
58221
+ if (allowedMcpTools.length > 0) {
58222
+ systemMessage += `
56435
58223
  ## MCP Tools (JSON parameters in <params> tag)
56436
58224
  `;
56437
- systemMessage += this.mcpBridge.getXmlToolDefinitions();
56438
- systemMessage += `
58225
+ systemMessage += this.mcpBridge.getXmlToolDefinitions(allowedMcpTools);
58226
+ systemMessage += `
56439
58227
 
56440
58228
  For MCP tools, use JSON format within the params tag, e.g.:
56441
58229
  <mcp_tool>
@@ -56444,6 +58232,7 @@ For MCP tools, use JSON format within the params tag, e.g.:
56444
58232
  </params>
56445
58233
  </mcp_tool>
56446
58234
  `;
58235
+ }
56447
58236
  }
56448
58237
  const searchDirectory = this.allowedFolders.length > 0 ? this.allowedFolders[0] : process.cwd();
56449
58238
  if (this.debug) {
@@ -56600,44 +58389,83 @@ You are working with a repository located at: ${searchDirectory}
56600
58389
  }
56601
58390
  }
56602
58391
  let assistantResponseContent = "";
56603
- try {
56604
- const executeAIRequest = async () => {
56605
- const messagesForAI = this.prepareMessagesWithImages(currentMessages);
56606
- const result = await streamText({
56607
- model: this.provider(this.model),
56608
- messages: messagesForAI,
56609
- maxTokens: maxResponseTokens,
56610
- temperature: 0.3
56611
- });
56612
- const usagePromise = result.usage;
56613
- for await (const delta of result.textStream) {
56614
- assistantResponseContent += delta;
56615
- if (options.onStream) {
56616
- options.onStream(delta);
58392
+ let compactionAttempted = false;
58393
+ while (true) {
58394
+ try {
58395
+ const executeAIRequest = async () => {
58396
+ const messagesForAI = this.prepareMessagesWithImages(currentMessages);
58397
+ const result = await this.streamTextWithRetryAndFallback({
58398
+ model: this.provider(this.model),
58399
+ messages: messagesForAI,
58400
+ maxTokens: maxResponseTokens,
58401
+ temperature: 0.3
58402
+ });
58403
+ const usagePromise = result.usage;
58404
+ for await (const delta of result.textStream) {
58405
+ assistantResponseContent += delta;
58406
+ if (options.onStream) {
58407
+ options.onStream(delta);
58408
+ }
58409
+ }
58410
+ const usage = await usagePromise;
58411
+ if (usage) {
58412
+ this.tokenCounter.recordUsage(usage, result.experimental_providerMetadata);
56617
58413
  }
58414
+ return result;
58415
+ };
58416
+ if (this.tracer) {
58417
+ await this.tracer.withSpan("ai.request", executeAIRequest, {
58418
+ "ai.model": this.model,
58419
+ "ai.provider": this.clientApiProvider || "auto",
58420
+ "iteration": currentIteration,
58421
+ "max_tokens": maxResponseTokens,
58422
+ "temperature": 0.3,
58423
+ "message_count": currentMessages.length
58424
+ });
58425
+ } else {
58426
+ await executeAIRequest();
56618
58427
  }
56619
- const usage = await usagePromise;
56620
- if (usage) {
56621
- this.tokenCounter.recordUsage(usage, result.experimental_providerMetadata);
58428
+ break;
58429
+ } catch (error) {
58430
+ if (!compactionAttempted && handleContextLimitError) {
58431
+ const compactionResult = handleContextLimitError(error, currentMessages, {
58432
+ keepLastSegment: true,
58433
+ minSegmentsToKeep: 1
58434
+ });
58435
+ if (compactionResult) {
58436
+ const { messages: compactedMessages, stats } = compactionResult;
58437
+ if (stats.removed === 0) {
58438
+ console.error(`[ERROR] Context window exceeded but no messages can be compacted.`);
58439
+ console.error(`[ERROR] The conversation history is already minimal (${stats.originalCount} messages).`);
58440
+ finalResult = `Error: Context window limit exceeded and conversation cannot be compacted further. Consider starting a new session or reducing system message size.`;
58441
+ throw new Error(finalResult);
58442
+ }
58443
+ compactionAttempted = true;
58444
+ console.log(`[INFO] Context window limit exceeded. Compacting conversation...`);
58445
+ console.log(`[INFO] Removed ${stats.removed} messages (${stats.reductionPercent}% reduction)`);
58446
+ console.log(`[INFO] Estimated token savings: ${stats.tokensSaved} tokens`);
58447
+ if (this.debug) {
58448
+ console.log(`[DEBUG] Compaction stats:`, stats);
58449
+ console.log(`[DEBUG] Original message count: ${stats.originalCount}`);
58450
+ console.log(`[DEBUG] Compacted message count: ${stats.compactedCount}`);
58451
+ }
58452
+ currentMessages = [...compactedMessages];
58453
+ if (this.tracer) {
58454
+ this.tracer.addEvent("context.compacted", {
58455
+ "iteration": currentIteration,
58456
+ "original_count": stats.originalCount,
58457
+ "compacted_count": stats.compactedCount,
58458
+ "reduction_percent": stats.reductionPercent,
58459
+ "tokens_saved": stats.tokensSaved
58460
+ });
58461
+ }
58462
+ continue;
58463
+ }
56622
58464
  }
56623
- return result;
56624
- };
56625
- if (this.tracer) {
56626
- await this.tracer.withSpan("ai.request", executeAIRequest, {
56627
- "ai.model": this.model,
56628
- "ai.provider": this.clientApiProvider || "auto",
56629
- "iteration": currentIteration,
56630
- "max_tokens": maxResponseTokens,
56631
- "temperature": 0.3,
56632
- "message_count": currentMessages.length
56633
- });
56634
- } else {
56635
- await executeAIRequest();
58465
+ console.error(`Error during streamText (Iter ${currentIteration}):`, error);
58466
+ finalResult = `Error: Failed to get response from AI model during iteration ${currentIteration}. ${error.message}`;
58467
+ throw new Error(finalResult);
56636
58468
  }
56637
- } catch (error) {
56638
- console.error(`Error during streamText (Iter ${currentIteration}):`, error);
56639
- finalResult = `Error: Failed to get response from AI model during iteration ${currentIteration}. ${error.message}`;
56640
- throw new Error(finalResult);
56641
58469
  }
56642
58470
  if (this.debug && assistantResponseContent) {
56643
58471
  const assistantPreview = createMessagePreview(assistantResponseContent);
@@ -56655,7 +58483,10 @@ You are working with a repository located at: ${searchDirectory}
56655
58483
  "attempt_completion"
56656
58484
  ];
56657
58485
  if (this.allowEdit) {
56658
- validTools.push("implement");
58486
+ validTools.push("implement", "edit", "create");
58487
+ }
58488
+ if (this.enableBash) {
58489
+ validTools.push("bash");
56659
58490
  }
56660
58491
  if (this.enableDelegate) {
56661
58492
  validTools.push("delegate");
@@ -56999,7 +58830,6 @@ Convert your previous response content into actual JSON data that follows this s
56999
58830
  ...options,
57000
58831
  _schemaFormatted: true
57001
58832
  });
57002
- finalResult = cleanSchemaResponse(finalResult);
57003
58833
  if (!this.disableMermaidValidation) {
57004
58834
  try {
57005
58835
  if (this.debug) {
@@ -57055,14 +58885,11 @@ Convert your previous response content into actual JSON data that follows this s
57055
58885
  } else if (this.debug) {
57056
58886
  console.log(`[DEBUG] Mermaid validation: Skipped due to disableMermaidValidation option`);
57057
58887
  }
58888
+ finalResult = cleanSchemaResponse(finalResult);
57058
58889
  if (isJsonSchema(options.schema)) {
57059
58890
  if (this.debug) {
57060
58891
  console.log(`[DEBUG] JSON validation: Starting validation process for schema response`);
57061
- console.log(`[DEBUG] JSON validation: Response length: ${finalResult.length} chars`);
57062
- }
57063
- finalResult = cleanSchemaResponse(finalResult);
57064
- if (this.debug) {
57065
- console.log(`[DEBUG] JSON validation: After cleaning, length: ${finalResult.length} chars`);
58892
+ console.log(`[DEBUG] JSON validation: Cleaned response length: ${finalResult.length} chars`);
57066
58893
  }
57067
58894
  if (this.tracer) {
57068
58895
  this.tracer.recordJsonValidationEvent("started", {
@@ -57152,10 +58979,9 @@ Convert your previous response content into actual JSON data that follows this s
57152
58979
  console.log("[DEBUG] Skipping schema formatting due to max iterations reached without completion");
57153
58980
  } else if (completionAttempted && options.schema && !options._schemaFormatted && !options._skipValidation) {
57154
58981
  try {
57155
- finalResult = cleanSchemaResponse(finalResult);
57156
58982
  if (!this.disableMermaidValidation) {
57157
58983
  if (this.debug) {
57158
- console.log(`[DEBUG] Mermaid validation: Validating attempt_completion result...`);
58984
+ console.log(`[DEBUG] Mermaid validation: Validating attempt_completion result BEFORE schema cleaning...`);
57159
58985
  }
57160
58986
  const mermaidValidation = await validateAndFixMermaidResponse(finalResult, {
57161
58987
  debug: this.debug,
@@ -57178,6 +59004,7 @@ Convert your previous response content into actual JSON data that follows this s
57178
59004
  } else if (this.debug) {
57179
59005
  console.log(`[DEBUG] Mermaid validation: Skipped for attempt_completion result due to disableMermaidValidation option`);
57180
59006
  }
59007
+ finalResult = cleanSchemaResponse(finalResult);
57181
59008
  if (isJsonSchema(options.schema)) {
57182
59009
  if (this.debug) {
57183
59010
  console.log(`[DEBUG] JSON validation: Starting validation process for attempt_completion result`);
@@ -57351,6 +59178,56 @@ Convert your previous response content into actual JSON data that follows this s
57351
59178
  console.log(`[DEBUG] Cleared conversation history and reset counters for session ${this.sessionId}`);
57352
59179
  }
57353
59180
  }
59181
+ /**
59182
+ * Manually compact conversation history
59183
+ * Removes intermediate monologues from older segments while preserving
59184
+ * user messages, final answers, and the most recent segment
59185
+ *
59186
+ * @param {Object} options - Compaction options
59187
+ * @param {boolean} [options.keepLastSegment=true] - Keep the most recent segment intact
59188
+ * @param {number} [options.minSegmentsToKeep=1] - Number of recent segments to preserve fully
59189
+ * @returns {Object} Compaction statistics
59190
+ */
59191
+ async compactHistory(options = {}) {
59192
+ const { compactMessages: compactMessages2, calculateCompactionStats: calculateCompactionStats2 } = await Promise.resolve().then(() => (init_contextCompactor(), contextCompactor_exports));
59193
+ if (this.history.length === 0) {
59194
+ if (this.debug) {
59195
+ console.log(`[DEBUG] No history to compact for session ${this.sessionId}`);
59196
+ }
59197
+ return {
59198
+ originalCount: 0,
59199
+ compactedCount: 0,
59200
+ removed: 0,
59201
+ reductionPercent: 0,
59202
+ originalTokens: 0,
59203
+ compactedTokens: 0,
59204
+ tokensSaved: 0
59205
+ };
59206
+ }
59207
+ const compactedMessages = compactMessages2(this.history, options);
59208
+ const stats = calculateCompactionStats2(this.history, compactedMessages);
59209
+ this.history = compactedMessages;
59210
+ try {
59211
+ await this.storageAdapter.clearHistory(this.sessionId);
59212
+ for (const message of compactedMessages) {
59213
+ await this.storageAdapter.saveMessage(this.sessionId, message);
59214
+ }
59215
+ } catch (error) {
59216
+ console.error(`[ERROR] Failed to save compacted messages to storage:`, error);
59217
+ }
59218
+ console.log(`[INFO] Manually compacted conversation history`);
59219
+ console.log(`[INFO] Removed ${stats.removed} messages (${stats.reductionPercent}% reduction)`);
59220
+ console.log(`[INFO] Estimated token savings: ${stats.tokensSaved} tokens`);
59221
+ if (this.debug) {
59222
+ console.log(`[DEBUG] Compaction stats:`, stats);
59223
+ }
59224
+ await this.hooks.emit(HOOK_TYPES.STORAGE_SAVE, {
59225
+ sessionId: this.sessionId,
59226
+ compacted: true,
59227
+ stats
59228
+ });
59229
+ return stats;
59230
+ }
57354
59231
  /**
57355
59232
  * Clone this agent's session to create a new agent with shared conversation history
57356
59233
  * @param {Object} options - Clone options
@@ -57373,6 +59250,14 @@ Convert your previous response content into actual JSON data that follows this s
57373
59250
  if (stripInternalMessages) {
57374
59251
  clonedHistory = this._stripInternalMessages(clonedHistory, keepSystemMessage);
57375
59252
  }
59253
+ let allowedToolsArray = null;
59254
+ if (this.allowedTools.mode === "whitelist") {
59255
+ allowedToolsArray = [...this.allowedTools.allowed];
59256
+ } else if (this.allowedTools.mode === "none") {
59257
+ allowedToolsArray = [];
59258
+ } else if (this.allowedTools.mode === "all" && this.allowedTools.exclusions.length > 0) {
59259
+ allowedToolsArray = ["*", ...this.allowedTools.exclusions.map((t) => "!" + t)];
59260
+ }
57376
59261
  const clonedAgent = new _ProbeAgent({
57377
59262
  // Copy current agent's config
57378
59263
  customPrompt: this.customPrompt,
@@ -57390,6 +59275,7 @@ Convert your previous response content into actual JSON data that follows this s
57390
59275
  maxIterations: this.maxIterations,
57391
59276
  disableMermaidValidation: this.disableMermaidValidation,
57392
59277
  disableJsonValidation: this.disableJsonValidation,
59278
+ allowedTools: allowedToolsArray,
57393
59279
  enableMcp: !!this.mcpBridge,
57394
59280
  mcpConfig: this.mcpConfig,
57395
59281
  enableBash: this.enableBash,
@@ -57569,8 +59455,8 @@ import {
57569
59455
  ListToolsRequestSchema,
57570
59456
  McpError
57571
59457
  } from "@modelcontextprotocol/sdk/types.js";
57572
- import { readFileSync as readFileSync2, existsSync as existsSync5 } from "fs";
57573
- import { resolve as resolve4 } from "path";
59458
+ import { readFileSync as readFileSync2, existsSync as existsSync6 } from "fs";
59459
+ import { resolve as resolve5 } from "path";
57574
59460
 
57575
59461
  // src/agent/acp/server.js
57576
59462
  import { randomUUID as randomUUID5 } from "crypto";
@@ -57829,8 +59715,8 @@ var ACPConnection = class extends EventEmitter4 {
57829
59715
  if (params !== null) {
57830
59716
  message.params = params;
57831
59717
  }
57832
- return new Promise((resolve5, reject2) => {
57833
- this.pendingRequests.set(id, { resolve: resolve5, reject: reject2 });
59718
+ return new Promise((resolve6, reject2) => {
59719
+ this.pendingRequests.set(id, { resolve: resolve6, reject: reject2 });
57834
59720
  this.sendMessage(message);
57835
59721
  setTimeout(() => {
57836
59722
  if (this.pendingRequests.has(id)) {
@@ -58244,8 +60130,8 @@ dotenv3.config();
58244
60130
  function readInputContent(input) {
58245
60131
  if (!input) return null;
58246
60132
  try {
58247
- const resolvedPath = resolve4(input);
58248
- if (existsSync5(resolvedPath)) {
60133
+ const resolvedPath = resolve5(input);
60134
+ if (existsSync6(resolvedPath)) {
58249
60135
  return readFileSync2(resolvedPath, "utf-8").trim();
58250
60136
  }
58251
60137
  } catch (error) {
@@ -58253,7 +60139,7 @@ function readInputContent(input) {
58253
60139
  return input;
58254
60140
  }
58255
60141
  function readFromStdin() {
58256
- return new Promise((resolve5, reject2) => {
60142
+ return new Promise((resolve6, reject2) => {
58257
60143
  let data = "";
58258
60144
  let hasReceivedData = false;
58259
60145
  let dataChunks = [];
@@ -58278,7 +60164,7 @@ function readFromStdin() {
58278
60164
  if (!trimmed && dataChunks.length === 0) {
58279
60165
  reject2(new Error("No input received from stdin"));
58280
60166
  } else {
58281
- resolve5(trimmed);
60167
+ resolve6(trimmed);
58282
60168
  }
58283
60169
  });
58284
60170
  process.stdin.on("error", (error) => {
@@ -58324,6 +60210,10 @@ function parseArgs() {
58324
60210
  // New flag to enable outline format
58325
60211
  noMermaidValidation: false,
58326
60212
  // New flag to disable mermaid validation
60213
+ allowedTools: null,
60214
+ // Tool filtering: ['*'] = all, [] = none, ['tool1', 'tool2'] = specific
60215
+ disableTools: false,
60216
+ // Convenience flag to disable all tools
58327
60217
  // Bash tool configuration
58328
60218
  enableBash: false,
58329
60219
  bashAllow: null,
@@ -58377,6 +60267,17 @@ function parseArgs() {
58377
60267
  config.outline = true;
58378
60268
  } else if (arg === "--no-mermaid-validation") {
58379
60269
  config.noMermaidValidation = true;
60270
+ } else if (arg === "--allowed-tools" && i + 1 < args.length) {
60271
+ const toolsArg = args[++i];
60272
+ if (toolsArg === "*" || toolsArg === "all") {
60273
+ config.allowedTools = ["*"];
60274
+ } else if (toolsArg === "none" || toolsArg === "") {
60275
+ config.allowedTools = [];
60276
+ } else {
60277
+ config.allowedTools = toolsArg.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
60278
+ }
60279
+ } else if (arg === "--disable-tools") {
60280
+ config.disableTools = true;
58380
60281
  } else if (arg === "--enable-bash") {
58381
60282
  config.enableBash = true;
58382
60283
  } else if (arg === "--bash-allow" && i + 1 < args.length) {
@@ -58421,6 +60322,13 @@ Options:
58421
60322
  --model <name> Override model name
58422
60323
  --allow-edit Enable code modification capabilities
58423
60324
  --enable-delegate Enable delegate tool for task distribution to subagents
60325
+ --allowed-tools <tools> Filter available tools (comma-separated list)
60326
+ Use '*' or 'all' for all tools (default)
60327
+ Use 'none' or '' for no tools (raw AI mode)
60328
+ Specific tools: search,query,extract,listFiles,searchFiles
60329
+ Supports exclusion: '*,!bash' (all except bash)
60330
+ --disable-tools Disable all tools (raw AI mode, no code analysis)
60331
+ Convenience flag equivalent to --allowed-tools none
58424
60332
  --verbose Enable verbose output
58425
60333
  --outline Use outline-xml format for code search results
58426
60334
  --mcp Run as MCP server
@@ -58462,6 +60370,9 @@ Examples:
58462
60370
  probe agent "Analyze codebase" --schema schema.json # Schema from file
58463
60371
  probe agent "Debug issue" --trace-file ./debug.jsonl --verbose
58464
60372
  probe agent "Analyze code" --trace-remote http://localhost:4318/v1/traces
60373
+ probe agent "Explain this code" --allowed-tools search,extract # Only search and extract
60374
+ probe agent "What is this project about?" --allowed-tools none # Raw AI mode (no tools)
60375
+ probe agent "Tell me about this project" --disable-tools # Raw AI mode (convenience flag)
58465
60376
  probe agent --mcp # Start MCP server mode
58466
60377
  probe agent --acp # Start ACP server mode
58467
60378
 
@@ -58588,7 +60499,9 @@ var ProbeAgentMcpServer = class {
58588
60499
  allowEdit: !!args.allow_edit,
58589
60500
  debug: process.env.DEBUG === "1",
58590
60501
  maxResponseTokens: args.max_response_tokens,
58591
- disableMermaidValidation: !!args.no_mermaid_validation
60502
+ disableMermaidValidation: !!args.no_mermaid_validation,
60503
+ allowedTools: args.allowed_tools,
60504
+ disableTools: args.disable_tools
58592
60505
  };
58593
60506
  this.agent = new ProbeAgent(agentConfig2);
58594
60507
  await this.agent.initialize();
@@ -58804,7 +60717,7 @@ async function main() {
58804
60717
  bashConfig.timeout = timeout;
58805
60718
  }
58806
60719
  if (config.bashWorkingDir) {
58807
- if (!existsSync5(config.bashWorkingDir)) {
60720
+ if (!existsSync6(config.bashWorkingDir)) {
58808
60721
  console.error(`Error: Bash working directory does not exist: ${config.bashWorkingDir}`);
58809
60722
  process.exit(1);
58810
60723
  }
@@ -58826,6 +60739,8 @@ async function main() {
58826
60739
  outline: config.outline,
58827
60740
  maxResponseTokens: config.maxResponseTokens,
58828
60741
  disableMermaidValidation: config.noMermaidValidation,
60742
+ allowedTools: config.allowedTools,
60743
+ disableTools: config.disableTools,
58829
60744
  enableBash: config.enableBash,
58830
60745
  bashConfig
58831
60746
  };