openfox 2.0.14 → 2.0.16

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 (30) hide show
  1. package/dist/{chat-handler-ITXCBFJK.js → chat-handler-PC6OVDUR.js} +8 -6
  2. package/dist/{chunk-7GJ6XYAF.js → chunk-63UEI4AF.js} +20 -16
  3. package/dist/{chunk-4U45A7B3.js → chunk-DQ6SV26Y.js} +111 -45
  4. package/dist/{chunk-3QB2RMX2.js → chunk-F4PMNP7S.js} +2 -2
  5. package/dist/{chunk-TIKQWNYK.js → chunk-J7KOV4ST.js} +2 -2
  6. package/dist/{chunk-4ZTQO5JP.js → chunk-JO6RF2U2.js} +463 -15
  7. package/dist/{chunk-LNEBVOH6.js → chunk-OUK5IW26.js} +6 -6
  8. package/dist/{chunk-PP6VQXQW.js → chunk-SNQT7LNU.js} +2 -1
  9. package/dist/{chunk-4W2Z4B5U.js → chunk-WOTBK3QQ.js} +36 -41
  10. package/dist/{chunk-5BDVM6YI.js → chunk-YD6NDTKF.js} +1 -1
  11. package/dist/cli/dev.js +1 -1
  12. package/dist/cli/index.js +1 -1
  13. package/dist/{config-YFHA5MWV.js → config-Z66BQTNX.js} +2 -2
  14. package/dist/{orchestrator-VP74KOXI.js → orchestrator-3SB2BMQ7.js} +7 -5
  15. package/dist/package.json +3 -2
  16. package/dist/{processor-CCXZYLX6.js → processor-KWPYXPL7.js} +8 -6
  17. package/dist/{protocol-BE5dRtYw.d.ts → protocol-BC1QSQ-Y.d.ts} +7 -1
  18. package/dist/{protocol-YYWMFR35.js → protocol-BKNLAEPJ.js} +3 -3
  19. package/dist/{provider-QUJQLVOM.js → provider-YBTRC77Y.js} +3 -3
  20. package/dist/{serve-GA2UIHHB.js → serve-HZXJXK6T.js} +9 -9
  21. package/dist/server/index.d.ts +19 -1
  22. package/dist/server/index.js +8 -7
  23. package/dist/{server-EA6W7YIC.js → server-RYKKAOFF.js} +10 -8
  24. package/dist/shared/index.d.ts +2 -2
  25. package/dist/shared/index.js +1 -1
  26. package/dist/{tools-DUC54GEH.js → tools-Y7ZGLK7A.js} +6 -4
  27. package/dist/web/assets/{index-D0qVcBva.js → index-BMz5-yAf.js} +31 -31
  28. package/dist/web/index.html +1 -1
  29. package/dist/web/sw.js +1 -1
  30. package/package.json +3 -2
@@ -31,6 +31,9 @@ import {
31
31
  getMaxPerSession,
32
32
  getSessionProcessCount
33
33
  } from "./chunk-VRGRAQDG.js";
34
+ import {
35
+ createMcpTools
36
+ } from "./chunk-NWO6GRYE.js";
34
37
  import {
35
38
  getCurrentContextWindowId,
36
39
  getCurrentWindowMessageOptions,
@@ -52,7 +55,7 @@ import {
52
55
  createChatMessageUpdatedMessage,
53
56
  createChatPathConfirmationMessage,
54
57
  createQueueStateMessage
55
- } from "./chunk-3QB2RMX2.js";
58
+ } from "./chunk-F4PMNP7S.js";
56
59
  import {
57
60
  AskUserInterrupt,
58
61
  askUserTool
@@ -62,6 +65,10 @@ import {
62
65
  deleteSetting,
63
66
  getSetting
64
67
  } from "./chunk-RFNEDBVO.js";
68
+ import {
69
+ loadGlobalConfig,
70
+ saveGlobalConfig
71
+ } from "./chunk-SNQT7LNU.js";
65
72
  import {
66
73
  getGlobalConfigDir
67
74
  } from "./chunk-CQGTEGKL.js";
@@ -602,6 +609,182 @@ function isNodeError(error) {
602
609
  return error instanceof Error && "code" in error;
603
610
  }
604
611
 
612
+ // src/server/utils/encoding.ts
613
+ import { detect } from "jschardet";
614
+ import * as iconv from "iconv-lite";
615
+ function detectEncoding(buffer) {
616
+ if (buffer.length === 0) {
617
+ return { encoding: "utf-8", confidence: 1, bomSize: 0 };
618
+ }
619
+ const bomSize = detectBOM(buffer);
620
+ const bomStripped = bomSize > 0 ? buffer.subarray(bomSize) : buffer;
621
+ if (bomSize > 0) {
622
+ const bomEncoding = bomEncodingForSize(buffer);
623
+ return { encoding: bomEncoding, confidence: 1, bomSize };
624
+ }
625
+ if (isValidUtf8(bomStripped)) {
626
+ return { encoding: "utf-8", confidence: 0.95, bomSize: 0 };
627
+ }
628
+ const detected = detect(bomStripped);
629
+ if (detected && detected.encoding && detected.confidence > 0.2) {
630
+ return { encoding: normalizeEncoding(detected.encoding), confidence: detected.confidence, bomSize: 0 };
631
+ }
632
+ return { encoding: "ISO-8859-1", confidence: 0.1, bomSize: 0 };
633
+ }
634
+ function detectBOM(buffer) {
635
+ if (buffer.length >= 4 && buffer[0] === 0 && buffer[1] === 0 && buffer[2] === 254 && buffer[3] === 255)
636
+ return 4;
637
+ if (buffer.length >= 4 && buffer[0] === 255 && buffer[1] === 254 && buffer[2] === 0 && buffer[3] === 0)
638
+ return 4;
639
+ if (buffer.length >= 2 && buffer[0] === 254 && buffer[1] === 255) return 2;
640
+ if (buffer.length >= 2 && buffer[0] === 255 && buffer[1] === 254) return 2;
641
+ if (buffer.length >= 3 && buffer[0] === 239 && buffer[1] === 187 && buffer[2] === 191) return 3;
642
+ return 0;
643
+ }
644
+ function bomEncodingForSize(buffer) {
645
+ if (buffer.length >= 2) {
646
+ if (buffer[0] === 254 && buffer[1] === 255) return "utf-16be";
647
+ if (buffer[0] === 255 && buffer[1] === 254) {
648
+ if (buffer.length >= 4 && buffer[2] === 0 && buffer[3] === 0) return "utf-32";
649
+ return "utf-16le";
650
+ }
651
+ }
652
+ if (buffer.length >= 3 && buffer[0] === 239 && buffer[1] === 187 && buffer[2] === 191) return "utf-8";
653
+ if (buffer.length >= 4 && buffer[0] === 0 && buffer[1] === 0 && buffer[2] === 254 && buffer[3] === 255)
654
+ return "utf-32be";
655
+ return "utf-8";
656
+ }
657
+ function isValidUtf8(buffer) {
658
+ const decoder = new TextDecoder("utf-8", { fatal: true });
659
+ try {
660
+ decoder.decode(buffer);
661
+ return true;
662
+ } catch {
663
+ return false;
664
+ }
665
+ }
666
+ function normalizeEncoding(enc) {
667
+ const lower = enc.toLowerCase().replace(/_/g, "-");
668
+ const map = {
669
+ "utf-8": "utf-8",
670
+ utf8: "utf-8",
671
+ ascii: "utf-8",
672
+ "iso-8859-1": "ISO-8859-1",
673
+ "iso8859-1": "ISO-8859-1",
674
+ latin1: "ISO-8859-1",
675
+ "windows-1252": "windows-1252",
676
+ "shift-jis": "Shift_JIS",
677
+ shift_jis: "Shift_JIS",
678
+ "euc-jp": "EUC-JP",
679
+ "euc-kr": "EUC-KR",
680
+ gb2312: "GB2312",
681
+ gb18030: "GB18030",
682
+ big5: "Big5",
683
+ "utf-16": "utf-16le",
684
+ "utf-16le": "utf-16le",
685
+ "utf-16be": "utf-16be",
686
+ "utf-32": "utf-32le",
687
+ "utf-32le": "utf-32le",
688
+ "utf-32be": "utf-32be",
689
+ "koi8-r": "KOI8-R",
690
+ "koi8-u": "KOI8-U"
691
+ };
692
+ return map[lower] ?? enc;
693
+ }
694
+ function decodeContent(buffer, encoding) {
695
+ const bomSize = detectBOM(buffer);
696
+ const contentBuf = bomSize > 0 ? buffer.subarray(bomSize) : buffer;
697
+ const normalized = normalizeEncoding(encoding);
698
+ if (normalized === "utf-8") {
699
+ return contentBuf.toString("utf-8");
700
+ }
701
+ if (normalized === "utf-16le") {
702
+ return contentBuf.toString("utf-16le");
703
+ }
704
+ if (normalized === "utf-16be") {
705
+ return swapUtf16Endianness(contentBuf).toString("utf-16le");
706
+ }
707
+ if (normalized === "utf-32le") {
708
+ return utf32ToString(contentBuf, true);
709
+ }
710
+ if (normalized === "utf-32be") {
711
+ return utf32ToString(contentBuf, false);
712
+ }
713
+ return iconv.decode(contentBuf, normalized);
714
+ }
715
+ function encodeContent(content, encoding, addBom = false) {
716
+ const normalized = normalizeEncoding(encoding);
717
+ let encoded;
718
+ if (normalized === "utf-8") {
719
+ encoded = Buffer.from(content, "utf-8");
720
+ } else if (normalized === "utf-16le") {
721
+ encoded = Buffer.from(content, "utf-16le");
722
+ } else if (normalized === "utf-16be") {
723
+ encoded = swapUtf16Endianness(Buffer.from(content, "utf-16le"));
724
+ } else if (normalized === "utf-32le") {
725
+ encoded = stringToUtf32(content, true);
726
+ } else if (normalized === "utf-32be") {
727
+ encoded = stringToUtf32(content, false);
728
+ } else {
729
+ encoded = iconv.encode(content, normalized);
730
+ }
731
+ if (addBom) {
732
+ const bom = bomForEncoding(normalized);
733
+ if (bom) {
734
+ encoded = Buffer.concat([bom, encoded]);
735
+ }
736
+ }
737
+ return encoded;
738
+ }
739
+ function bomForEncoding(encoding) {
740
+ switch (encoding) {
741
+ case "utf-8":
742
+ return Buffer.from([239, 187, 191]);
743
+ case "utf-16le":
744
+ return Buffer.from([255, 254]);
745
+ case "utf-16be":
746
+ return Buffer.from([254, 255]);
747
+ case "utf-32le":
748
+ return Buffer.from([255, 254, 0, 0]);
749
+ case "utf-32be":
750
+ return Buffer.from([0, 0, 254, 255]);
751
+ default:
752
+ return null;
753
+ }
754
+ }
755
+ function swapUtf16Endianness(buf) {
756
+ const out = Buffer.alloc(buf.length);
757
+ for (let i = 0; i + 1 < buf.length; i += 2) {
758
+ out[i] = buf[i + 1];
759
+ out[i + 1] = buf[i];
760
+ }
761
+ return out;
762
+ }
763
+ function utf32ToString(buffer, littleEndian) {
764
+ const chars = [];
765
+ for (let i = 0; i + 3 < buffer.length; i += 4) {
766
+ const code = littleEndian ? buffer.readUInt32LE(i) : buffer.readUInt32BE(i);
767
+ if (code > 1114111) continue;
768
+ chars.push(String.fromCodePoint(code));
769
+ }
770
+ return chars.join("");
771
+ }
772
+ function stringToUtf32(str, littleEndian) {
773
+ const codes = [];
774
+ for (const ch of str) {
775
+ codes.push(ch.codePointAt(0));
776
+ }
777
+ const buf = Buffer.alloc(codes.length * 4);
778
+ for (let i = 0; i < codes.length; i++) {
779
+ if (littleEndian) {
780
+ buf.writeUInt32LE(codes[i], i * 4);
781
+ } else {
782
+ buf.writeUInt32BE(codes[i], i * 4);
783
+ }
784
+ }
785
+ return buf;
786
+ }
787
+
605
788
  // src/server/tools/read.ts
606
789
  var IMAGE_MIME_TYPES = {
607
790
  ".png": "image/png",
@@ -752,7 +935,8 @@ var readFileTool = createTool(
752
935
  }
753
936
  });
754
937
  }
755
- const content = rawBuffer.toString("utf-8");
938
+ const { encoding, confidence } = detectEncoding(rawBuffer);
939
+ const content = decodeContent(rawBuffer, encoding);
756
940
  const lines = content.split("\n");
757
941
  const totalLines = lines.length;
758
942
  const startLine = Math.max(1, offset);
@@ -774,7 +958,13 @@ var readFileTool = createTool(
774
958
  if (contentHash2) {
775
959
  context.sessionManager.recordFileRead(context.sessionId, fullPath, contentHash2);
776
960
  }
777
- return helpers.success(output, truncated);
961
+ return helpers.success(output, truncated, {
962
+ metadata: {
963
+ encoding,
964
+ confidence: Math.round(confidence * 100) / 100,
965
+ lineCount: totalLines
966
+ }
967
+ });
778
968
  }
779
969
  );
780
970
 
@@ -806,6 +996,14 @@ function formatDiagnosticsForLLM(diagnostics) {
806
996
  }
807
997
  return output;
808
998
  }
999
+ function appendLspInstallHint(output, lspManager, path) {
1000
+ if (!lspManager) return output;
1001
+ const hint = lspManager.getInstallHint(path);
1002
+ if (!hint) return output;
1003
+ return output + `
1004
+
1005
+ \u26A0\uFE0F Language server not installed. To enable diagnostics: \`${hint}\``;
1006
+ }
809
1007
 
810
1008
  // src/server/tools/write.ts
811
1009
  var writeFileTool = createTool(
@@ -825,6 +1023,10 @@ var writeFileTool = createTool(
825
1023
  content: {
826
1024
  type: "string",
827
1025
  description: "Content to write to the file"
1026
+ },
1027
+ encoding: {
1028
+ type: "string",
1029
+ description: 'Optional file encoding (e.g. "ISO-8859-1", "windows-1252", "utf-16"). Defaults to "utf-8". Use the encoding reported by read_file to match project conventions.'
828
1030
  }
829
1031
  },
830
1032
  required: ["path", "content"]
@@ -841,14 +1043,17 @@ var writeFileTool = createTool(
841
1043
  }
842
1044
  const dir = dirname(fullPath);
843
1045
  await mkdir(dir, { recursive: true });
844
- await writeFile(fullPath, args.content, "utf-8");
1046
+ const encoding = args.encoding ?? "utf-8";
1047
+ const encoded = encodeContent(args.content, encoding);
1048
+ await writeFile(fullPath, encoded);
845
1049
  const lineCount = args.content.split("\n").length;
846
- const byteCount = Buffer.byteLength(args.content, "utf-8");
1050
+ const byteCount = encoded.length;
847
1051
  let output = `Successfully wrote ${lineCount} lines (${byteCount} bytes) to ${args.path}`;
848
1052
  let diagnostics = [];
849
1053
  if (context.lspManager) {
850
1054
  diagnostics = await context.lspManager.notifyFileChange(fullPath, args.content);
851
1055
  output += formatDiagnosticsForLLM(diagnostics);
1056
+ output = appendLspInstallHint(output, context.lspManager, fullPath);
852
1057
  }
853
1058
  const newHash = await computeFileHash(fullPath);
854
1059
  if (newHash) {
@@ -1029,12 +1234,14 @@ var editFileTool = createTool(
1029
1234
  if (!validation.valid) {
1030
1235
  return helpers.error(validation.error?.message ?? "File validation failed");
1031
1236
  }
1032
- let content;
1237
+ let rawBuffer;
1033
1238
  try {
1034
- content = await readFile3(fullPath, "utf-8");
1239
+ rawBuffer = await readFile3(fullPath);
1035
1240
  } catch {
1036
1241
  return helpers.error(`File not found: ${args.path}`);
1037
1242
  }
1243
+ const { encoding, bomSize } = detectEncoding(rawBuffer);
1244
+ const content = decodeContent(rawBuffer, encoding);
1038
1245
  const fileLineEnding = detectLineEnding(content);
1039
1246
  const normalizedContent = normalizeToLF(content);
1040
1247
  const normalizedOldString = normalizeToLF(args.old_string);
@@ -1092,16 +1299,18 @@ Make sure whitespace and indentation match exactly.`
1092
1299
  }
1093
1300
  replacedContent = normalizedContent.slice(0, index) + normalizedNewString + normalizedContent.slice(index + normalizedOldString.length);
1094
1301
  }
1095
- const newContent = replacedContent.replace(
1302
+ const restoredContent = replacedContent.replace(
1096
1303
  /\n/g,
1097
1304
  fileLineEnding === "crlf" ? "\r\n" : fileLineEnding === "cr" ? "\r" : "\n"
1098
1305
  );
1099
- await writeFile2(fullPath, newContent, "utf-8");
1306
+ const encoded = encodeContent(restoredContent, encoding, bomSize > 0);
1307
+ await writeFile2(fullPath, encoded);
1100
1308
  let output = `Successfully replaced ${replaceAll ? occurrences : 1} occurrence(s) in ${args.path}`;
1101
1309
  let diagnostics = [];
1102
1310
  if (context.lspManager) {
1103
- diagnostics = await context.lspManager.notifyFileChange(fullPath, newContent);
1311
+ diagnostics = await context.lspManager.notifyFileChange(fullPath, restoredContent);
1104
1312
  output += formatDiagnosticsForLLM(diagnostics);
1313
+ output = appendLspInstallHint(output, context.lspManager, fullPath);
1105
1314
  }
1106
1315
  const newHash = await computeFileHash(fullPath);
1107
1316
  if (newHash) {
@@ -2677,10 +2886,10 @@ async function describeImageFromDataUrl(dataUrl, visionModel, options) {
2677
2886
  import { createHash as createHash2 } from "crypto";
2678
2887
  async function loadVisionModelFromGlobalConfig() {
2679
2888
  try {
2680
- const { loadGlobalConfig, getVisionFallback } = await import("./config-YFHA5MWV.js");
2889
+ const { loadGlobalConfig: loadGlobalConfig2, getVisionFallback } = await import("./config-Z66BQTNX.js");
2681
2890
  const runtimeConfig = getRuntimeConfig();
2682
2891
  const mode = runtimeConfig.mode ?? "production";
2683
- const globalConfig = await loadGlobalConfig(mode);
2892
+ const globalConfig = await loadGlobalConfig2(mode);
2684
2893
  const fallback = getVisionFallback(globalConfig);
2685
2894
  if (fallback?.enabled && fallback.model) {
2686
2895
  return { baseUrl: fallback.url, model: fallback.model, timeout: fallback.timeout * 1e3 };
@@ -3132,7 +3341,7 @@ var callSubAgentTool = {
3132
3341
  };
3133
3342
  }
3134
3343
  try {
3135
- const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-DUC54GEH.js");
3344
+ const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-Y7ZGLK7A.js");
3136
3345
  const toolRegistry = getToolRegistryForAgent2(agentDef);
3137
3346
  const turnMetrics = new TurnMetrics();
3138
3347
  const result = await executeSubAgent({
@@ -3919,6 +4128,238 @@ These processes run independently of agent turns and persist across session comp
3919
4128
  }
3920
4129
  );
3921
4130
 
4131
+ // src/server/chat/dynamic-context.ts
4132
+ import { createHash as createHash3 } from "crypto";
4133
+ function computeDynamicContextHash(instructionContent, skills, toolFingerprint) {
4134
+ const dynamicInputs = JSON.stringify({
4135
+ instructions: instructionContent,
4136
+ skills: skills.map((s) => s.id).sort(),
4137
+ ...toolFingerprint ? { tools: toolFingerprint } : {}
4138
+ });
4139
+ return createHash3("sha256").update(dynamicInputs).digest("hex");
4140
+ }
4141
+ function getToolFingerprint(tools) {
4142
+ return tools.map((t) => `${t.function.name}:${JSON.stringify(t.function.parameters)}`).sort().join("|");
4143
+ }
4144
+ async function computeContextHash(sessionManager, sessionId) {
4145
+ const session = sessionManager.requireSession(sessionId);
4146
+ const { content: instructionContent } = await getAllInstructions(session.workdir, session.projectId);
4147
+ const runtimeConfig = getRuntimeConfig();
4148
+ const configDir = getGlobalConfigDir(runtimeConfig.mode ?? "production");
4149
+ const skills = await getEnabledSkillMetadata(configDir, runtimeConfig.workdir);
4150
+ const { createToolRegistry: createToolRegistry2 } = await import("./tools-Y7ZGLK7A.js");
4151
+ const allTools = createToolRegistry2().definitions;
4152
+ const toolFingerprint = getToolFingerprint(allTools);
4153
+ const hash = computeDynamicContextHash(instructionContent, skills, toolFingerprint);
4154
+ return { hash, instructionContent, skills, allTools };
4155
+ }
4156
+ async function applyDynamicContext(sessionManager, sessionId) {
4157
+ const { hash, instructionContent, skills, allTools } = await computeContextHash(sessionManager, sessionId);
4158
+ const allAgents = await loadAllAgentsDefault();
4159
+ const subAgentDefs = getSubAgents(allAgents);
4160
+ const systemPrompt = buildTopLevelSystemPrompt(
4161
+ sessionManager.requireSession(sessionId).workdir,
4162
+ instructionContent || void 0,
4163
+ skills,
4164
+ subAgentDefs
4165
+ );
4166
+ sessionManager.setCachedPrompt(sessionId, systemPrompt, allTools, hash);
4167
+ sessionManager.setDynamicContextChanged(sessionId, false);
4168
+ sessionManager.clearDebugDump(sessionId);
4169
+ logger.debug("applyDynamicContext done", { sessionId, hash, toolCount: allTools.length });
4170
+ }
4171
+
4172
+ // src/server/tools/mcp-config.ts
4173
+ var mcpManagerForTools = null;
4174
+ var mcpConfigMode = "production";
4175
+ var mcpNotifyChanged = null;
4176
+ function setMcpManagerForTools(manager) {
4177
+ mcpManagerForTools = manager;
4178
+ }
4179
+ function setMcpConfigMode(mode) {
4180
+ mcpConfigMode = mode;
4181
+ }
4182
+ function setNotifyMcpServersChanged(fn) {
4183
+ mcpNotifyChanged = fn;
4184
+ }
4185
+ var mcpConfigTool = createTool(
4186
+ "mcp_config",
4187
+ {
4188
+ type: "function",
4189
+ function: {
4190
+ name: "mcp_config",
4191
+ description: "Configure MCP servers (Model Context Protocol). Actions: list (show all servers and tools), add (add a server), remove (delete a server), toggle-tool (enable/disable a tool). Use this when the user asks to add, remove, or configure MCP servers or tools.",
4192
+ parameters: {
4193
+ type: "object",
4194
+ properties: {
4195
+ action: {
4196
+ type: "string",
4197
+ enum: ["list", "add", "remove", "toggle-tool"],
4198
+ description: "Action: list (show servers), add (add new server), remove (delete a server), toggle-tool (enable/disable a tool)"
4199
+ },
4200
+ name: {
4201
+ type: "string",
4202
+ description: "Server name (required for: add, remove, toggle-tool)"
4203
+ },
4204
+ transport: {
4205
+ type: "string",
4206
+ enum: ["stdio", "http"],
4207
+ description: "Transport type (for add). Default: stdio"
4208
+ },
4209
+ command: {
4210
+ type: "string",
4211
+ description: 'Command for stdio transport (e.g. "npx")'
4212
+ },
4213
+ args: {
4214
+ type: "array",
4215
+ items: { type: "string" },
4216
+ description: "Command arguments for stdio transport"
4217
+ },
4218
+ env: {
4219
+ type: "object",
4220
+ additionalProperties: { type: "string" },
4221
+ description: "Environment variables for stdio transport"
4222
+ },
4223
+ url: {
4224
+ type: "string",
4225
+ description: 'Server URL for HTTP transport (e.g. "https://mcp.example.com/mcp")'
4226
+ },
4227
+ headers: {
4228
+ type: "object",
4229
+ additionalProperties: { type: "string" },
4230
+ description: 'HTTP headers for HTTP transport (e.g. {"Authorization": "Bearer xxx"})'
4231
+ },
4232
+ toolName: {
4233
+ type: "string",
4234
+ description: "Tool name to toggle (required for: toggle-tool)"
4235
+ },
4236
+ enabled: {
4237
+ type: "boolean",
4238
+ description: "Whether the tool should be enabled (required for: toggle-tool)"
4239
+ }
4240
+ },
4241
+ required: ["action"]
4242
+ }
4243
+ }
4244
+ },
4245
+ async (args, context, helpers) => {
4246
+ const actionError = validateActionWithPermission(
4247
+ args.action,
4248
+ ["list", "add", "remove", "toggle-tool"],
4249
+ "mcp_config",
4250
+ context.permittedActions
4251
+ );
4252
+ if (actionError) return actionError;
4253
+ if (!mcpManagerForTools) {
4254
+ return helpers.error("MCP manager not available");
4255
+ }
4256
+ async function persistAndRebuild(updater) {
4257
+ const globalConfig = await loadGlobalConfig(mcpConfigMode);
4258
+ const mcpServers = { ...globalConfig.mcpServers ?? {} };
4259
+ const updated = updater(mcpServers);
4260
+ await saveGlobalConfig(mcpConfigMode, { ...globalConfig, mcpServers: updated });
4261
+ }
4262
+ async function rebuildTools() {
4263
+ const { setMcpTools: setMcpTools2 } = await import("./tools-Y7ZGLK7A.js");
4264
+ const mcpTools = createMcpTools(mcpManagerForTools);
4265
+ setMcpTools2(mcpTools);
4266
+ }
4267
+ if (args.action === "list") {
4268
+ const servers = mcpManagerForTools.getAllServers();
4269
+ if (servers.length === 0) {
4270
+ return helpers.success("No MCP servers configured.");
4271
+ }
4272
+ const lines = [];
4273
+ for (const server of servers) {
4274
+ const connStr = server.status === "connected" ? "\u25CF" : server.status === "error" ? "\u2717" : "\u25CB";
4275
+ const cmdStr = server.config.command ? `${server.config.command} ${(server.config.args ?? []).join(" ")}` : server.config.url ?? "";
4276
+ lines.push(
4277
+ `${connStr} ${server.name} (${server.config.transport}) \u2014 ${server.status}${server.error ? `: ${server.error}` : ""}`
4278
+ );
4279
+ lines.push(` ${cmdStr}`);
4280
+ lines.push(` ${server.tools.length} tools, ~${server.estimatedTokens} tokens`);
4281
+ const enabledTools = server.tools.filter((t) => t.enabled);
4282
+ const disabledTools = server.tools.filter((t) => !t.enabled);
4283
+ if (enabledTools.length > 0) {
4284
+ lines.push(` Enabled: ${enabledTools.map((t) => t.name).join(", ")}`);
4285
+ }
4286
+ if (disabledTools.length > 0) {
4287
+ lines.push(` Disabled: ${disabledTools.map((t) => t.name).join(", ")}`);
4288
+ }
4289
+ }
4290
+ return helpers.success(lines.join("\n"));
4291
+ }
4292
+ if (args.action === "add") {
4293
+ if (!args.name) return helpers.error("Missing required field: name");
4294
+ if (args.transport === "http") {
4295
+ if (!args.url) return helpers.error("url is required for http transport");
4296
+ } else if (!args.command) {
4297
+ return helpers.error("command is required for stdio transport");
4298
+ }
4299
+ const serverCfg = {
4300
+ transport: args.transport ?? "stdio",
4301
+ ...args.command ? { command: args.command } : {},
4302
+ ...args.args && args.args.length > 0 ? { args: args.args } : {},
4303
+ ...args.env && Object.keys(args.env).length > 0 ? { env: args.env } : {},
4304
+ ...args.url ? { url: args.url } : {},
4305
+ ...args.headers && Object.keys(args.headers).length > 0 ? { headers: args.headers } : {}
4306
+ };
4307
+ await persistAndRebuild((mcpServers) => {
4308
+ mcpServers[args.name] = serverCfg;
4309
+ return mcpServers;
4310
+ });
4311
+ await mcpManagerForTools.addServer(args.name, serverCfg);
4312
+ await rebuildTools();
4313
+ await applyDynamicContext(context.sessionManager, context.sessionId);
4314
+ mcpNotifyChanged?.(context.sessionId);
4315
+ const server = mcpManagerForTools.getServer(args.name);
4316
+ const toolCount = server?.tools.length ?? 0;
4317
+ return helpers.success(`Added MCP server "${args.name}" (${toolCount} tools discovered).`);
4318
+ }
4319
+ if (args.action === "remove") {
4320
+ if (!args.name) return helpers.error("Missing required field: name");
4321
+ await persistAndRebuild((mcpServers) => {
4322
+ delete mcpServers[args.name];
4323
+ return mcpServers;
4324
+ });
4325
+ mcpManagerForTools.removeServer(args.name);
4326
+ await rebuildTools();
4327
+ await applyDynamicContext(context.sessionManager, context.sessionId);
4328
+ mcpNotifyChanged?.(context.sessionId);
4329
+ return helpers.success(`Removed MCP server "${args.name}".`);
4330
+ }
4331
+ if (args.action === "toggle-tool") {
4332
+ if (!args.name) return helpers.error("Missing required field: name");
4333
+ if (!args.toolName) return helpers.error("Missing required field: toolName");
4334
+ if (args.enabled === void 0) return helpers.error("Missing required field: enabled");
4335
+ const server = mcpManagerForTools.getServer(args.name);
4336
+ const currentDisabled = (server?.tools ?? []).filter((t) => !t.enabled).map((t) => t.name);
4337
+ const afterDisabled = args.enabled ? currentDisabled.filter((n) => n !== args.toolName) : [...currentDisabled, args.toolName];
4338
+ await persistAndRebuild((mcpServers) => {
4339
+ const cfg = mcpServers[args.name];
4340
+ if (cfg) {
4341
+ const updated = { ...cfg };
4342
+ if (afterDisabled.length > 0) {
4343
+ updated.disabledTools = afterDisabled;
4344
+ } else {
4345
+ delete updated.disabledTools;
4346
+ }
4347
+ mcpServers[args.name] = updated;
4348
+ }
4349
+ return mcpServers;
4350
+ });
4351
+ await mcpManagerForTools.setToolEnabled(args.name, args.toolName, args.enabled);
4352
+ await rebuildTools();
4353
+ await applyDynamicContext(context.sessionManager, context.sessionId);
4354
+ mcpNotifyChanged?.(context.sessionId);
4355
+ return helpers.success(
4356
+ `Tool "${args.toolName}" ${args.enabled ? "enabled" : "disabled"} on server "${args.name}".`
4357
+ );
4358
+ }
4359
+ return helpers.error("Unexpected error");
4360
+ }
4361
+ );
4362
+
3922
4363
  // src/server/tools/index.ts
3923
4364
  function parseToolPermissions(allowedTools) {
3924
4365
  const result = {};
@@ -4075,7 +4516,8 @@ function getAllToolsMap() {
4075
4516
  webFetchTool,
4076
4517
  devServerTool,
4077
4518
  stepDoneTool,
4078
- backgroundProcessTool
4519
+ backgroundProcessTool,
4520
+ mcpConfigTool
4079
4521
  ].map((t) => [t.name, t]);
4080
4522
  const mcpEntries = mcpToolsOverride.map((t) => [t.name, t]);
4081
4523
  return new Map([...builtInTools, ...mcpEntries]);
@@ -4183,6 +4625,12 @@ export {
4183
4625
  executeSubAgent,
4184
4626
  devServerManager,
4185
4627
  stepDoneTool,
4628
+ computeDynamicContextHash,
4629
+ getToolFingerprint,
4630
+ applyDynamicContext,
4631
+ setMcpManagerForTools,
4632
+ setMcpConfigMode,
4633
+ setNotifyMcpServersChanged,
4186
4634
  parseToolPermissions,
4187
4635
  getToolPermissions,
4188
4636
  validateToolAction,
@@ -4192,4 +4640,4 @@ export {
4192
4640
  getToolRegistryForAgent,
4193
4641
  createToolRegistry
4194
4642
  };
4195
- //# sourceMappingURL=chunk-4ZTQO5JP.js.map
4643
+ //# sourceMappingURL=chunk-JO6RF2U2.js.map
@@ -33,7 +33,7 @@ Options:
33
33
  }
34
34
  async function runNetworkSetup(mode) {
35
35
  const { loadAuthConfig, saveAuthConfig, encryptPassword } = await import("./auth-56SIRACI.js");
36
- const { saveGlobalConfig } = await import("./config-YFHA5MWV.js");
36
+ const { saveGlobalConfig } = await import("./config-Z66BQTNX.js");
37
37
  const { getAuthKeyPath } = await import("./paths-X46PPOI2.js");
38
38
  const existingAuth = await loadAuthConfig(mode);
39
39
  if (existingAuth) {
@@ -94,7 +94,7 @@ async function runNetworkSetup(mode) {
94
94
  console.log("\u2713 Configuration saved!\n");
95
95
  }
96
96
  async function runConfig(mode) {
97
- const { loadGlobalConfig, getActiveProvider, getDefaultModel } = await import("./config-YFHA5MWV.js");
97
+ const { loadGlobalConfig, getActiveProvider, getDefaultModel } = await import("./config-Z66BQTNX.js");
98
98
  const { getGlobalConfigPath } = await import("./paths-X46PPOI2.js");
99
99
  const config = await loadGlobalConfig(mode);
100
100
  const configPath = getGlobalConfigPath(mode);
@@ -152,7 +152,7 @@ async function runCli(options) {
152
152
  break;
153
153
  }
154
154
  case "provider": {
155
- const { runProviderCommand } = await import("./provider-QUJQLVOM.js");
155
+ const { runProviderCommand } = await import("./provider-YBTRC77Y.js");
156
156
  const [, subcommand] = positionals;
157
157
  await runProviderCommand(mode, subcommand);
158
158
  break;
@@ -191,12 +191,12 @@ async function runCli(options) {
191
191
  break;
192
192
  }
193
193
  default: {
194
- const { configFileExists } = await import("./config-YFHA5MWV.js");
194
+ const { configFileExists } = await import("./config-Z66BQTNX.js");
195
195
  const configExists = await configFileExists(mode);
196
196
  if (!configExists) {
197
197
  await runNetworkSetup(mode);
198
198
  }
199
- const { runServe } = await import("./serve-GA2UIHHB.js");
199
+ const { runServe } = await import("./serve-HZXJXK6T.js");
200
200
  const serveOptions = { mode };
201
201
  if (values.port) serveOptions.port = parseInt(values.port);
202
202
  if (values["no-browser"] === true) serveOptions.openBrowser = false;
@@ -208,4 +208,4 @@ async function runCli(options) {
208
208
  export {
209
209
  runCli
210
210
  };
211
- //# sourceMappingURL=chunk-LNEBVOH6.js.map
211
+ //# sourceMappingURL=chunk-OUK5IW26.js.map
@@ -67,6 +67,7 @@ var mcpServerSchema = z.object({
67
67
  args: z.array(z.string()).optional(),
68
68
  env: z.record(z.string(), z.string()).optional(),
69
69
  url: z.string().optional(),
70
+ headers: z.record(z.string(), z.string()).optional(),
70
71
  disabledTools: z.array(z.string()).optional()
71
72
  });
72
73
  var defaultVisionFallback = { enabled: false, url: "http://localhost:11434", model: "qwen3.5:0.8b", timeout: 120 };
@@ -435,4 +436,4 @@ export {
435
436
  activateProvider,
436
437
  mergeConfigs
437
438
  };
438
- //# sourceMappingURL=chunk-PP6VQXQW.js.map
439
+ //# sourceMappingURL=chunk-SNQT7LNU.js.map