openfox 2.0.68 → 2.0.70

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.
@@ -5,14 +5,15 @@ import {
5
5
  } from "./chunk-64GN6OPR.js";
6
6
  import {
7
7
  runChatTurn
8
- } from "./chunk-FCLCEIY4.js";
9
- import "./chunk-6LK47TNE.js";
8
+ } from "./chunk-UTSSFPYA.js";
9
+ import "./chunk-PVWAE43V.js";
10
10
  import "./chunk-QFSDJIUS.js";
11
11
  import "./chunk-4QBDV6LX.js";
12
12
  import "./chunk-KXVFHMSM.js";
13
13
  import "./chunk-TFCHWMOJ.js";
14
14
  import "./chunk-PK2RMVMB.js";
15
15
  import "./chunk-NWO6GRYE.js";
16
+ import "./chunk-UN26GIQG.js";
16
17
  import "./chunk-VLV3XWZS.js";
17
18
  import "./chunk-WGMJCFCE.js";
18
19
  import {
@@ -157,4 +158,4 @@ export {
157
158
  startChatSession,
158
159
  stopSessionExecution
159
160
  };
160
- //# sourceMappingURL=chat-handler-QFEHJBWN.js.map
161
+ //# sourceMappingURL=chat-handler-K3DWIGHG.js.map
@@ -3,10 +3,7 @@ import {
3
3
  computeSessionHash,
4
4
  runAgentTurn,
5
5
  runChatTurn
6
- } from "./chunk-FCLCEIY4.js";
7
- import {
8
- appendCompactionPrompt
9
- } from "./chunk-YQ3MEZJL.js";
6
+ } from "./chunk-UTSSFPYA.js";
10
7
  import {
11
8
  deleteItemFromDir,
12
9
  devServerManager,
@@ -16,7 +13,10 @@ import {
16
13
  jsonSerializer,
17
14
  loadAllAgentsDefault,
18
15
  saveItemToDir
19
- } from "./chunk-6LK47TNE.js";
16
+ } from "./chunk-PVWAE43V.js";
17
+ import {
18
+ appendCompactionPrompt
19
+ } from "./chunk-YQ3MEZJL.js";
20
20
  import {
21
21
  TurnMetrics,
22
22
  createMessageStartEvent
@@ -2158,4 +2158,4 @@ export {
2158
2158
  signalMcpReady,
2159
2159
  createWebSocketServer
2160
2160
  };
2161
- //# sourceMappingURL=chunk-COWJRBV4.js.map
2161
+ //# sourceMappingURL=chunk-46OR4W3L.js.map
@@ -36,6 +36,9 @@ import {
36
36
  import {
37
37
  createMcpTools
38
38
  } from "./chunk-NWO6GRYE.js";
39
+ import {
40
+ applyMcpServerUpdate
41
+ } from "./chunk-UN26GIQG.js";
39
42
  import {
40
43
  listBranches
41
44
  } from "./chunk-VLV3XWZS.js";
@@ -3779,7 +3782,7 @@ var callSubAgentTool = {
3779
3782
  };
3780
3783
  }
3781
3784
  try {
3782
- const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-QAEN5KHA.js");
3785
+ const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-CALGWIV2.js");
3783
3786
  const toolRegistry = getToolRegistryForAgent2(agentDef);
3784
3787
  const turnMetrics = new TurnMetrics();
3785
3788
  const result = await executeSubAgent({
@@ -4701,18 +4704,18 @@ var mcpConfigTool = createTool(
4701
4704
  type: "function",
4702
4705
  function: {
4703
4706
  name: "mcp_config",
4704
- 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.",
4707
+ description: "Configure MCP servers (Model Context Protocol). Actions: list (show all servers and tools), add (add a server), update (modify an existing server \u2014 all fields are optional and merged with the current config, transport-incompatible fields are cleared on transport change), remove (delete a server), toggle-tool (enable/disable a tool). Use this when the user asks to add, remove, update, or configure MCP servers or tools.",
4705
4708
  parameters: {
4706
4709
  type: "object",
4707
4710
  properties: {
4708
4711
  action: {
4709
4712
  type: "string",
4710
- enum: ["list", "add", "remove", "toggle-tool"],
4711
- description: "Action: list (show servers), add (add new server), remove (delete a server), toggle-tool (enable/disable a tool)"
4713
+ enum: ["list", "add", "update", "remove", "toggle-tool"],
4714
+ description: "Action: list (show servers), add (add new server), update (modify existing server), remove (delete a server), toggle-tool (enable/disable a tool)"
4712
4715
  },
4713
4716
  name: {
4714
4717
  type: "string",
4715
- description: "Server name (required for: add, remove, toggle-tool)"
4718
+ description: "Server name (required for: add, update, remove, toggle-tool)"
4716
4719
  },
4717
4720
  transport: {
4718
4721
  type: "string",
@@ -4758,7 +4761,7 @@ var mcpConfigTool = createTool(
4758
4761
  async (args, context, helpers) => {
4759
4762
  const actionError = validateActionWithPermission(
4760
4763
  args.action,
4761
- ["list", "add", "remove", "toggle-tool"],
4764
+ ["list", "add", "update", "remove", "toggle-tool"],
4762
4765
  "mcp_config",
4763
4766
  context.permittedActions
4764
4767
  );
@@ -4778,7 +4781,7 @@ var mcpConfigTool = createTool(
4778
4781
  mcpNotifyChanged?.(sessionId);
4779
4782
  }
4780
4783
  async function rebuildTools() {
4781
- const { setMcpTools: setMcpTools2 } = await import("./tools-QAEN5KHA.js");
4784
+ const { setMcpTools: setMcpTools2 } = await import("./tools-CALGWIV2.js");
4782
4785
  const mcpTools = createMcpTools(mcpManagerForTools);
4783
4786
  setMcpTools2(mcpTools);
4784
4787
  }
@@ -4834,6 +4837,38 @@ var mcpConfigTool = createTool(
4834
4837
  const toolCount = server?.tools.length ?? 0;
4835
4838
  return helpers.success(`Added MCP server "${args.name}" (${toolCount} tools discovered). ${APPLY_PROMPT_MESSAGE}`);
4836
4839
  }
4840
+ if (args.action === "update") {
4841
+ if (!args.name) return helpers.error("Missing required field: name");
4842
+ const existing = mcpManagerForTools.getServer(args.name);
4843
+ if (!existing) return helpers.error(`MCP server "${args.name}" not found`);
4844
+ const globalConfig = await loadGlobalConfig(mcpConfigMode, mcpConfigPath);
4845
+ const mcpServers = { ...globalConfig.mcpServers ?? {} };
4846
+ const patch = {
4847
+ ...args.transport !== void 0 ? { transport: args.transport } : {},
4848
+ ...args.command !== void 0 ? { command: args.command } : {},
4849
+ ...args.args !== void 0 ? { args: args.args } : {},
4850
+ ...args.env !== void 0 ? { env: args.env } : {},
4851
+ ...args.url !== void 0 ? { url: args.url } : {},
4852
+ ...args.headers !== void 0 ? { headers: args.headers } : {}
4853
+ };
4854
+ const { error: updateError } = await applyMcpServerUpdate({
4855
+ name: args.name,
4856
+ patch,
4857
+ existing,
4858
+ persistedCfg: mcpServers[args.name],
4859
+ mcpManager: mcpManagerForTools,
4860
+ save: async (cfg) => {
4861
+ mcpServers[args.name] = cfg;
4862
+ await saveGlobalConfig(mcpConfigMode, { ...globalConfig, mcpServers }, mcpConfigPath);
4863
+ }
4864
+ });
4865
+ if (updateError) return helpers.error(updateError);
4866
+ await rebuildTools();
4867
+ notifyContextChanged(context.sessionId);
4868
+ const server = mcpManagerForTools.getServer(args.name);
4869
+ const toolCount = server?.tools.length ?? 0;
4870
+ return helpers.success(`Updated MCP server "${args.name}" (${toolCount} tools discovered). ${APPLY_PROMPT_MESSAGE}`);
4871
+ }
4837
4872
  if (args.action === "remove") {
4838
4873
  if (!args.name) return helpers.error("Missing required field: name");
4839
4874
  await persistAndRebuild((mcpServers) => {
@@ -5441,4 +5476,4 @@ export {
5441
5476
  getToolRegistryForAgent,
5442
5477
  createToolRegistry
5443
5478
  };
5444
- //# sourceMappingURL=chunk-6LK47TNE.js.map
5479
+ //# sourceMappingURL=chunk-PVWAE43V.js.map
@@ -0,0 +1,54 @@
1
+ // src/server/mcp/update-server.ts
2
+ function buildUpdatedServerConfig(patch, existing, persistedCfg) {
3
+ const existingTransport = existing.config.transport ?? "stdio";
4
+ const resolvedTransport = patch.transport ?? existingTransport;
5
+ const transportChanged = resolvedTransport !== existingTransport;
6
+ const mergedCommand = patch.command !== void 0 ? patch.command : transportChanged ? void 0 : existing.config.command;
7
+ const mergedArgs = patch.args !== void 0 ? patch.args : transportChanged ? void 0 : existing.config.args;
8
+ const mergedEnv = patch.env !== void 0 ? patch.env : transportChanged ? void 0 : existing.config.env;
9
+ const mergedUrl = patch.url !== void 0 ? patch.url : transportChanged ? void 0 : existing.config.url;
10
+ const mergedHeaders = patch.headers !== void 0 ? patch.headers : transportChanged ? void 0 : existing.config.headers;
11
+ if (resolvedTransport === "http" && !mergedUrl) {
12
+ return { serverCfg: {}, error: "url is required for http transport" };
13
+ }
14
+ if (resolvedTransport !== "http" && !mergedCommand) {
15
+ return { serverCfg: {}, error: "command is required for stdio transport" };
16
+ }
17
+ const serverCfg = {
18
+ transport: resolvedTransport,
19
+ ...mergedCommand ? { command: mergedCommand } : {},
20
+ ...mergedArgs && mergedArgs.length > 0 ? { args: mergedArgs } : {},
21
+ ...mergedEnv && Object.keys(mergedEnv).length > 0 ? { env: mergedEnv } : {},
22
+ ...mergedUrl ? { url: mergedUrl } : {},
23
+ ...mergedHeaders && Object.keys(mergedHeaders).length > 0 ? { headers: mergedHeaders } : {},
24
+ ...persistedCfg?.disabledTools && persistedCfg.disabledTools.length > 0 ? { disabledTools: persistedCfg.disabledTools } : {},
25
+ ...persistedCfg?.cachedTools && persistedCfg.cachedTools.length > 0 ? { cachedTools: persistedCfg.cachedTools } : {}
26
+ };
27
+ return { serverCfg };
28
+ }
29
+ async function applyMcpServerUpdate(options) {
30
+ const { name, patch, existing, persistedCfg, mcpManager, save } = options;
31
+ const { serverCfg, error } = buildUpdatedServerConfig(patch, existing, persistedCfg);
32
+ if (error) return { serverCfg: {}, error };
33
+ mcpManager.removeServer(name);
34
+ try {
35
+ await mcpManager.addServer(name, serverCfg);
36
+ } catch (addError) {
37
+ await mcpManager.addServer(name, existing.config);
38
+ throw addError;
39
+ }
40
+ try {
41
+ await save(serverCfg);
42
+ } catch (saveError) {
43
+ mcpManager.removeServer(name);
44
+ await mcpManager.addServer(name, existing.config);
45
+ throw saveError;
46
+ }
47
+ return { serverCfg };
48
+ }
49
+
50
+ export {
51
+ buildUpdatedServerConfig,
52
+ applyMcpServerUpdate
53
+ };
54
+ //# sourceMappingURL=chunk-UN26GIQG.js.map
@@ -10,7 +10,7 @@ import {
10
10
  loadAllAgentsDefault,
11
11
  processEventsForConversation,
12
12
  runTopLevelAgentLoop
13
- } from "./chunk-6LK47TNE.js";
13
+ } from "./chunk-PVWAE43V.js";
14
14
  import {
15
15
  TurnMetrics,
16
16
  buildAgentReminder,
@@ -64,7 +64,7 @@ function resolveAgentDef(sessionManager, sessionId) {
64
64
  }
65
65
  async function buildCachedPrompt(sessionManager, sessionId, agentDef) {
66
66
  const { instructionContent, skills } = await loadSessionContext(sessionManager, sessionId);
67
- const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-QAEN5KHA.js");
67
+ const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-CALGWIV2.js");
68
68
  const tools = getToolRegistryForAgent2(agentDef).definitions;
69
69
  const toolFingerprint = getToolFingerprint(tools);
70
70
  const allAgents = await loadAllAgentsDefault();
@@ -77,7 +77,7 @@ async function buildCachedPrompt(sessionManager, sessionId, agentDef) {
77
77
  async function computeSessionHash(sessionManager, sessionId) {
78
78
  const { instructionContent, skills } = await loadSessionContext(sessionManager, sessionId);
79
79
  const agentDef = await resolveAgentDef(sessionManager, sessionId);
80
- const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-QAEN5KHA.js");
80
+ const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-CALGWIV2.js");
81
81
  const tools = getToolRegistryForAgent2(agentDef).definitions;
82
82
  const toolFingerprint = getToolFingerprint(tools);
83
83
  return computeDynamicContextHash(instructionContent, skills, toolFingerprint);
@@ -357,4 +357,4 @@ export {
357
357
  runChatTurn,
358
358
  runAgentTurn
359
359
  };
360
- //# sourceMappingURL=chunk-FCLCEIY4.js.map
360
+ //# sourceMappingURL=chunk-UTSSFPYA.js.map
@@ -208,7 +208,7 @@ async function runCli(options) {
208
208
  if (!configExists) {
209
209
  await runNetworkSetup(mode);
210
210
  }
211
- const { runServe } = await import("./serve-QVVZ5SJE.js");
211
+ const { runServe } = await import("./serve-NOHD22CT.js");
212
212
  const serveOptions = { mode };
213
213
  if (values.port) serveOptions.port = parseInt(values.port);
214
214
  if (values["no-browser"] === true) serveOptions.openBrowser = false;
@@ -220,4 +220,4 @@ async function runCli(options) {
220
220
  export {
221
221
  runCli
222
222
  };
223
- //# sourceMappingURL=chunk-32KLLK4N.js.map
223
+ //# sourceMappingURL=chunk-XDWV5CYV.js.map
@@ -21,7 +21,7 @@ import {
21
21
  tokenFromPassword,
22
22
  verifyPassword,
23
23
  workflowExists
24
- } from "./chunk-COWJRBV4.js";
24
+ } from "./chunk-46OR4W3L.js";
25
25
  import {
26
26
  agentExists,
27
27
  createToolRegistry,
@@ -66,7 +66,7 @@ import {
66
66
  setSkillEnabled,
67
67
  skillExists,
68
68
  updateOwnedSkill
69
- } from "./chunk-6LK47TNE.js";
69
+ } from "./chunk-PVWAE43V.js";
70
70
  import {
71
71
  ALWAYS_ALLOWED,
72
72
  ALWAYS_ALLOWED_FOR_SUBAGENTS,
@@ -1733,7 +1733,9 @@ var LspServer = class {
1733
1733
  }
1734
1734
  this.state = "starting";
1735
1735
  try {
1736
- this.process = spawn(this.commandPath, this.config.serverArgs, {
1736
+ const needsShell = process.platform === "win32" && /\.(cmd|bat)$/i.test(this.commandPath);
1737
+ const command = needsShell ? [`"${this.commandPath}"`, ...this.config.serverArgs].join(" ") : this.commandPath;
1738
+ this.process = spawn(command, needsShell ? [] : this.config.serverArgs, {
1737
1739
  cwd: this.workdir,
1738
1740
  stdio: ["pipe", "pipe", "pipe"],
1739
1741
  env: {
@@ -1741,11 +1743,21 @@ var LspServer = class {
1741
1743
  HOME: process.env["HOME"],
1742
1744
  PATH: process.env["PATH"]
1743
1745
  },
1744
- windowsHide: true
1746
+ windowsHide: true,
1747
+ shell: needsShell
1748
+ });
1749
+ await new Promise((resolve8, reject) => {
1750
+ this.process.once("spawn", resolve8);
1751
+ this.process.once("error", reject);
1745
1752
  });
1746
1753
  if (!this.process.stdin || !this.process.stdout) {
1747
1754
  throw new Error("Failed to get stdio streams from language server process");
1748
1755
  }
1756
+ const onStreamError = (err) => {
1757
+ logger.debug("LSP stream error", { language: this.config.id, error: err.message });
1758
+ };
1759
+ this.process.stdin.on("error", onStreamError);
1760
+ this.process.stdout.on("error", onStreamError);
1749
1761
  this.connection = createMessageConnection(
1750
1762
  new StreamMessageReader(this.process.stdout),
1751
1763
  new StreamMessageWriter(this.process.stdin)
@@ -2196,6 +2208,21 @@ async function existsExecutable(path) {
2196
2208
  return false;
2197
2209
  }
2198
2210
  }
2211
+ function candidateNames(command) {
2212
+ if (process.platform !== "win32") {
2213
+ return [command];
2214
+ }
2215
+ return [".cmd", ".exe", ".bat", ""].map((ext) => command + ext);
2216
+ }
2217
+ async function findInDir(dir, command) {
2218
+ for (const name of candidateNames(command)) {
2219
+ const fullPath = join2(dir, name);
2220
+ if (await existsExecutable(fullPath)) {
2221
+ return fullPath;
2222
+ }
2223
+ }
2224
+ return null;
2225
+ }
2199
2226
  async function which(command, workdir) {
2200
2227
  if (isAbsolutePath(command)) {
2201
2228
  if (await existsExecutable(command)) {
@@ -2204,24 +2231,24 @@ async function which(command, workdir) {
2204
2231
  return null;
2205
2232
  }
2206
2233
  for (const binDir of getBundledBinPaths()) {
2207
- const fullPath = join2(binDir, command);
2208
- if (await existsExecutable(fullPath)) {
2209
- return fullPath;
2234
+ const found = await findInDir(binDir, command);
2235
+ if (found) {
2236
+ return found;
2210
2237
  }
2211
2238
  }
2212
2239
  if (workdir) {
2213
- const projectBin = join2(workdir, "node_modules", ".bin", command);
2214
- if (await existsExecutable(projectBin)) {
2215
- return projectBin;
2240
+ const found = await findInDir(join2(workdir, "node_modules", ".bin"), command);
2241
+ if (found) {
2242
+ return found;
2216
2243
  }
2217
2244
  }
2218
2245
  const pathEnv = process.env["PATH"] ?? "";
2219
2246
  const paths = pathEnv.split(getPathSeparator());
2220
2247
  for (const dir of paths) {
2221
2248
  if (!dir) continue;
2222
- const fullPath = join2(dir, command);
2223
- if (await existsExecutable(fullPath)) {
2224
- return fullPath;
2249
+ const found = await findInDir(dir, command);
2250
+ if (found) {
2251
+ return found;
2225
2252
  }
2226
2253
  }
2227
2254
  return null;
@@ -4257,7 +4284,7 @@ import { Router as Router9 } from "express";
4257
4284
  import { spawn as spawn2 } from "child_process";
4258
4285
 
4259
4286
  // src/constants.ts
4260
- var VERSION = "2.0.68";
4287
+ var VERSION = "2.0.70";
4261
4288
 
4262
4289
  // src/server/routes/auto-update.ts
4263
4290
  var updateInProgress = false;
@@ -4605,6 +4632,7 @@ async function loadProviderPlugins(options) {
4605
4632
  import { Router as Router11 } from "express";
4606
4633
  import { fileURLToPath as fileURLToPath6, pathToFileURL as pathToFileURL2 } from "url";
4607
4634
  import { dirname as dirname7, resolve as resolve6, join as join9 } from "path";
4635
+ import { existsSync } from "fs";
4608
4636
  import { readFile as readFile4, readdir as readdir3, rm as rm2, rename as rename2 } from "fs/promises";
4609
4637
  import { platform } from "os";
4610
4638
  import { execFile } from "child_process";
@@ -4631,7 +4659,10 @@ function createPluginRoutes(options) {
4631
4659
  return res.json({ plugins: registryCache.data });
4632
4660
  }
4633
4661
  const moduleDir = dirname7(fileURLToPath6(import.meta.url));
4634
- const registryPath = resolve6(moduleDir, "../../../plugins-registry.json");
4662
+ let registryPath = resolve6(moduleDir, "../plugins-registry.json");
4663
+ if (!existsSync(registryPath)) {
4664
+ registryPath = resolve6(moduleDir, "../../../plugins-registry.json");
4665
+ }
4635
4666
  const data = JSON.parse(await readFile4(registryPath, "utf8"));
4636
4667
  registryCache = { data, ts: now };
4637
4668
  res.json({ plugins: data });
@@ -4890,7 +4921,7 @@ async function createServerHandle(config3) {
4890
4921
  setMcpTools(mcpTools);
4891
4922
  logger.info("MCP tools registered", { count: mcpTools.length });
4892
4923
  }
4893
- const { signalMcpReady } = await import("./server-ECKS2Y3R.js");
4924
+ const { signalMcpReady } = await import("./server-RLNPHLY2.js");
4894
4925
  signalMcpReady();
4895
4926
  });
4896
4927
  const app = express();
@@ -5162,7 +5193,7 @@ async function createServerHandle(config3) {
5162
5193
  app.get("/api/sessions/:id", async (req, res) => {
5163
5194
  const { getEventStore: getEventStore2 } = await import("./events-CDQ4HASP.js");
5164
5195
  const { buildMessagesFromStoredEvents } = await import("./folding-V2K34EGN.js");
5165
- const { getPendingQuestionsForSession } = await import("./tools-QAEN5KHA.js");
5196
+ const { getPendingQuestionsForSession } = await import("./tools-CALGWIV2.js");
5166
5197
  const session = sessionManager.getSession(req.params.id);
5167
5198
  if (!session) {
5168
5199
  return res.status(404).json({ error: "Session not found" });
@@ -5181,8 +5212,8 @@ async function createServerHandle(config3) {
5181
5212
  if (!session) {
5182
5213
  return res.status(404).json({ error: "Session not found" });
5183
5214
  }
5184
- const { stopSessionExecution } = await import("./chat-handler-QFEHJBWN.js");
5185
- const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-QAEN5KHA.js");
5215
+ const { stopSessionExecution } = await import("./chat-handler-K3DWIGHG.js");
5216
+ const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-CALGWIV2.js");
5186
5217
  sessionManager.clearMessageQueue(sessionId);
5187
5218
  stopSessionExecution(sessionId, sessionManager);
5188
5219
  abortSession(sessionId);
@@ -5278,6 +5309,38 @@ async function createServerHandle(config3) {
5278
5309
  sessionManager.setMetadataEntries(sessionId, "review_findings", entries);
5279
5310
  res.json({ success: true });
5280
5311
  });
5312
+ app.put("/api/sessions/:id/metadata/:key", async (req, res) => {
5313
+ const sessionId = req.params.id;
5314
+ const key = req.params.key;
5315
+ const session = sessionManager.getSession(sessionId);
5316
+ if (!session) {
5317
+ return res.status(404).json({ error: "Session not found" });
5318
+ }
5319
+ const { entries } = req.body;
5320
+ if (!Array.isArray(entries)) {
5321
+ return res.status(400).json({ error: "entries is required and must be an array" });
5322
+ }
5323
+ for (let i = 0; i < entries.length; i++) {
5324
+ const c = entries[i];
5325
+ if (c === null || typeof c !== "object" || Array.isArray(c)) {
5326
+ return res.status(400).json({ error: `entries[${i}] must be an object` });
5327
+ }
5328
+ if (c.description !== void 0 && typeof c.description !== "string") {
5329
+ return res.status(400).json({ error: `entries[${i}].description must be a string` });
5330
+ }
5331
+ if (c.status !== void 0 && typeof c.status !== "string") {
5332
+ return res.status(400).json({ error: `entries[${i}].status must be a string` });
5333
+ }
5334
+ }
5335
+ const mapped = entries.map((c, i) => ({
5336
+ ...c,
5337
+ id: c.id != null ? String(c.id) : String(i),
5338
+ description: c.description ?? "",
5339
+ status: c.status ?? "open"
5340
+ }));
5341
+ sessionManager.setMetadataEntries(sessionId, key, mapped);
5342
+ res.json({ success: true });
5343
+ });
5281
5344
  app.put("/api/sessions/:id/mode", async (req, res) => {
5282
5345
  const { getEventStore: getEventStore2 } = await import("./events-CDQ4HASP.js");
5283
5346
  const { buildMessagesFromStoredEvents } = await import("./folding-V2K34EGN.js");
@@ -5336,7 +5399,7 @@ async function createServerHandle(config3) {
5336
5399
  if (!callId || approved === void 0) {
5337
5400
  return res.status(400).json({ error: "callId and approved are required" });
5338
5401
  }
5339
- const { providePathConfirmation } = await import("./tools-QAEN5KHA.js");
5402
+ const { providePathConfirmation } = await import("./tools-CALGWIV2.js");
5340
5403
  const result = providePathConfirmation(callId, approved, alwaysAllow);
5341
5404
  if (!result.found) {
5342
5405
  return res.status(404).json({ error: "No pending path confirmation with that ID" });
@@ -5344,7 +5407,7 @@ async function createServerHandle(config3) {
5344
5407
  const { getEventStore: getEventStore2 } = await import("./events-CDQ4HASP.js");
5345
5408
  const { buildMessagesFromStoredEvents, foldPendingConfirmations } = await import("./folding-V2K34EGN.js");
5346
5409
  const { createSessionStateMessage } = await import("./protocol-CTNE3ANP.js");
5347
- const { getPendingQuestionsForSession } = await import("./tools-QAEN5KHA.js");
5410
+ const { getPendingQuestionsForSession } = await import("./tools-CALGWIV2.js");
5348
5411
  const eventStore = getEventStore2();
5349
5412
  const events = eventStore.getEvents(sessionId);
5350
5413
  const messages = buildMessagesFromStoredEvents(events);
@@ -5365,7 +5428,7 @@ async function createServerHandle(config3) {
5365
5428
  if (!skip && typeof answer !== "string") {
5366
5429
  return res.status(400).json({ error: "answer is required when not skipping" });
5367
5430
  }
5368
- const { provideAnswer } = await import("./tools-QAEN5KHA.js");
5431
+ const { provideAnswer } = await import("./tools-CALGWIV2.js");
5369
5432
  const found = provideAnswer(callId, answer ?? "", skip ?? false);
5370
5433
  if (!found) {
5371
5434
  return res.status(404).json({ error: "No pending question with that ID" });
@@ -5425,7 +5488,7 @@ async function createServerHandle(config3) {
5425
5488
  backend: activeProvider?.backend ?? llmClient.getBackend(),
5426
5489
  model: llmClient.getModel()
5427
5490
  };
5428
- const { runAgentTurn, TurnMetrics } = await import("./orchestrator-GNXKJPX4.js");
5491
+ const { runAgentTurn, TurnMetrics } = await import("./orchestrator-PUV4WG77.js");
5429
5492
  runAgentTurn(
5430
5493
  {
5431
5494
  sessionManager,
@@ -5463,8 +5526,8 @@ async function createServerHandle(config3) {
5463
5526
  if (!session) {
5464
5527
  return res.status(404).json({ error: "Session not found" });
5465
5528
  }
5466
- const { stopSessionExecution } = await import("./chat-handler-QFEHJBWN.js");
5467
- const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-QAEN5KHA.js");
5529
+ const { stopSessionExecution } = await import("./chat-handler-K3DWIGHG.js");
5530
+ const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-CALGWIV2.js");
5468
5531
  const queuedMessages = sessionManager.getQueueState(sessionId);
5469
5532
  sessionManager.clearMessageQueue(sessionId);
5470
5533
  stopSessionExecution(sessionId, sessionManager);
@@ -6087,7 +6150,7 @@ async function createServerHandle(config3) {
6087
6150
  });
6088
6151
  async function rebuildMcpTools() {
6089
6152
  const { createMcpTools: createMcpTools2 } = await import("./tool-adapter-MOGEI6TP.js");
6090
- const { setMcpTools: setMcpTools2 } = await import("./tools-QAEN5KHA.js");
6153
+ const { setMcpTools: setMcpTools2 } = await import("./tools-CALGWIV2.js");
6091
6154
  const mcpTools = createMcpTools2(mcpManager);
6092
6155
  setMcpTools2(mcpTools);
6093
6156
  }
@@ -6172,6 +6235,69 @@ async function createServerHandle(config3) {
6172
6235
  res.status(400).json({ error: error instanceof Error ? error.message : String(error) });
6173
6236
  }
6174
6237
  });
6238
+ app.put("/api/mcp/servers/:name", async (req, res) => {
6239
+ const { name } = req.params;
6240
+ const existing = mcpManager.getServer(name);
6241
+ if (!existing) {
6242
+ return res.status(404).json({ error: `MCP server '${name}' not found` });
6243
+ }
6244
+ const body = req.body;
6245
+ const { transport: rawTransport, command, args, env, url, headers } = body;
6246
+ if (rawTransport !== void 0 && rawTransport !== "stdio" && rawTransport !== "http") {
6247
+ return res.status(400).json({ error: `Invalid transport '${String(rawTransport)}'. Must be 'stdio' or 'http'.` });
6248
+ }
6249
+ if (command !== void 0 && typeof command !== "string") {
6250
+ return res.status(400).json({ error: "command must be a string" });
6251
+ }
6252
+ if (args !== void 0 && (!Array.isArray(args) || args.some((a) => typeof a !== "string"))) {
6253
+ return res.status(400).json({ error: "args must be an array of strings" });
6254
+ }
6255
+ if (env !== void 0 && (typeof env !== "object" || env === null || Array.isArray(env) || Object.values(env).some((v) => typeof v !== "string"))) {
6256
+ return res.status(400).json({ error: "env must be a string/string object" });
6257
+ }
6258
+ if (url !== void 0 && typeof url !== "string") {
6259
+ return res.status(400).json({ error: "url must be a string" });
6260
+ }
6261
+ if (headers !== void 0 && (typeof headers !== "object" || headers === null || Array.isArray(headers) || Object.values(headers).some((v) => typeof v !== "string"))) {
6262
+ return res.status(400).json({ error: "headers must be a string/string object" });
6263
+ }
6264
+ try {
6265
+ const { loadGlobalConfig, saveGlobalConfig } = await import("./config-EUMVEFHU.js");
6266
+ const { applyMcpServerUpdate } = await import("./update-server-MYYQUNZX.js");
6267
+ const globalConfig = await loadGlobalConfig(config3.mode ?? "production", config3.globalConfigPath);
6268
+ const mcpServers2 = { ...globalConfig.mcpServers ?? {} };
6269
+ const patch = {
6270
+ ...rawTransport !== void 0 ? { transport: rawTransport } : {},
6271
+ ...command !== void 0 ? { command } : {},
6272
+ ...args !== void 0 ? { args } : {},
6273
+ ...env !== void 0 ? { env } : {},
6274
+ ...url !== void 0 ? { url } : {},
6275
+ ...headers !== void 0 ? { headers } : {}
6276
+ };
6277
+ const { error: updateError } = await applyMcpServerUpdate({
6278
+ name,
6279
+ patch,
6280
+ existing,
6281
+ persistedCfg: mcpServers2[name],
6282
+ mcpManager,
6283
+ save: async (cfg) => {
6284
+ mcpServers2[name] = cfg;
6285
+ await saveGlobalConfig(config3.mode ?? "production", { ...globalConfig, mcpServers: mcpServers2 }, config3.globalConfigPath);
6286
+ }
6287
+ });
6288
+ if (updateError) {
6289
+ return res.status(400).json({ error: updateError });
6290
+ }
6291
+ await rebuildMcpTools();
6292
+ const sessions = sessionManager.listSessions();
6293
+ for (const s of sessions) {
6294
+ sessionManager.setDynamicContextChanged(s.id, true);
6295
+ }
6296
+ res.json({ server: mcpManager.getServer(name) });
6297
+ } catch (error) {
6298
+ res.status(400).json({ error: error instanceof Error ? error.message : String(error) });
6299
+ }
6300
+ });
6175
6301
  app.delete("/api/mcp/servers/:name", async (req, res) => {
6176
6302
  const { name } = req.params;
6177
6303
  const server = mcpManager.getServer(name);
@@ -6408,7 +6534,7 @@ async function createServerHandle(config3) {
6408
6534
  const state = sessionManager.getContextState(sessionId);
6409
6535
  wssExports.broadcastForSession(sessionId, createContextStateMessage(state));
6410
6536
  });
6411
- const { QueueProcessor } = await import("./processor-TIQEQDP7.js");
6537
+ const { QueueProcessor } = await import("./processor-O2QW27BD.js");
6412
6538
  const queueProcessor = new QueueProcessor({
6413
6539
  sessionManager,
6414
6540
  providerManager,
@@ -6489,4 +6615,4 @@ export {
6489
6615
  createServerHandle,
6490
6616
  createServer
6491
6617
  };
6492
- //# sourceMappingURL=chunk-DWZEGLHJ.js.map
6618
+ //# sourceMappingURL=chunk-ZZ5JPVBT.js.map
package/dist/cli/dev.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runCli
4
- } from "../chunk-32KLLK4N.js";
4
+ } from "../chunk-XDWV5CYV.js";
5
5
  import "../chunk-XY3LESVZ.js";
6
6
  import {
7
7
  logger
package/dist/cli/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runCli
4
- } from "../chunk-32KLLK4N.js";
4
+ } from "../chunk-XDWV5CYV.js";
5
5
  import "../chunk-XY3LESVZ.js";
6
6
  import {
7
7
  logger
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  runAgentTurn,
3
3
  runChatTurn
4
- } from "./chunk-FCLCEIY4.js";
5
- import "./chunk-6LK47TNE.js";
4
+ } from "./chunk-UTSSFPYA.js";
5
+ import "./chunk-PVWAE43V.js";
6
6
  import {
7
7
  TurnMetrics,
8
8
  createChatDoneEvent,
@@ -16,6 +16,7 @@ import "./chunk-KXVFHMSM.js";
16
16
  import "./chunk-TFCHWMOJ.js";
17
17
  import "./chunk-PK2RMVMB.js";
18
18
  import "./chunk-NWO6GRYE.js";
19
+ import "./chunk-UN26GIQG.js";
19
20
  import "./chunk-VLV3XWZS.js";
20
21
  import "./chunk-WGMJCFCE.js";
21
22
  import "./chunk-TFDK6VSG.js";
@@ -44,4 +45,4 @@ export {
44
45
  runAgentTurn,
45
46
  runChatTurn
46
47
  };
47
- //# sourceMappingURL=orchestrator-GNXKJPX4.js.map
48
+ //# sourceMappingURL=orchestrator-PUV4WG77.js.map
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openfox",
3
- "version": "2.0.68",
3
+ "version": "2.0.70",
4
4
  "description": "Local-LLM-first agentic coding assistant",
5
5
  "type": "module",
6
6
  "bin": {
@@ -32,7 +32,7 @@
32
32
  "dev": "OPENFOX_DEV=true tsx watch src/cli/dev.ts --no-browser",
33
33
  "dev:web": "vite --config web/vite.config.ts",
34
34
  "build": "npm run build:server && npm run build:web",
35
- "build:server": "tsup && mkdir -p dist/command-defaults dist/skill-defaults dist/agent-defaults dist/workflow-defaults && cp src/server/commands/defaults/*.md dist/command-defaults/ && cp -r src/server/skills/defaults/* dist/skill-defaults/ && cp src/server/agents/defaults/*.md dist/agent-defaults/ && cp src/server/workflows/defaults/*.json dist/workflow-defaults/ && cp src/server/lsp/languages.json dist/languages.json && cp src/cli/update.sh dist/cli/ && chmod +x dist/cli/update.sh && cp package.json dist/ && cp -r src/server/public dist/server/",
35
+ "build:server": "tsup && mkdir -p dist/command-defaults dist/skill-defaults dist/agent-defaults dist/workflow-defaults && cp src/server/commands/defaults/*.md dist/command-defaults/ && cp -r src/server/skills/defaults/* dist/skill-defaults/ && cp src/server/agents/defaults/*.md dist/agent-defaults/ && cp src/server/workflows/defaults/*.json dist/workflow-defaults/ && cp src/server/lsp/languages.json dist/languages.json && cp src/cli/update.sh dist/cli/ && chmod +x dist/cli/update.sh && cp package.json dist/ && cp plugins-registry.json dist/ && cp -r src/server/public dist/server/",
36
36
  "build:web": "cd web && vite build --outDir ../dist/web",
37
37
  "start": "node dist/cli/index.js",
38
38
  "start:dev": "node dist/cli/dev.js",