openfox 2.0.60 → 2.0.61

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.
@@ -7,6 +7,7 @@ color: '#3b82f6'
7
7
  allowedTools:
8
8
  - read_file
9
9
  - web_fetch
10
+ - web_search
10
11
  - write_file
11
12
  - edit_file
12
13
  - run_command
@@ -7,6 +7,7 @@ color: '#a855f7'
7
7
  allowedTools:
8
8
  - read_file
9
9
  - web_fetch
10
+ - web_search
10
11
  - run_command
11
12
  - ask_user
12
13
  - session_metadata
@@ -5,8 +5,8 @@ import {
5
5
  } from "./chunk-NVPVDDQZ.js";
6
6
  import {
7
7
  runChatTurn
8
- } from "./chunk-NY6IGGKZ.js";
9
- import "./chunk-3JRYK5UG.js";
8
+ } from "./chunk-ZHSCXD75.js";
9
+ import "./chunk-KX4C5Q5J.js";
10
10
  import "./chunk-LBGHZLLH.js";
11
11
  import "./chunk-BGTEIA2S.js";
12
12
  import "./chunk-PK2RMVMB.js";
@@ -17,7 +17,7 @@ import {
17
17
  createSessionRunningMessage
18
18
  } from "./chunk-F4PMNP7S.js";
19
19
  import "./chunk-EU3WWTFH.js";
20
- import "./chunk-WNSAJCWT.js";
20
+ import "./chunk-SVSKQYFH.js";
21
21
  import "./chunk-YQ3SOPBI.js";
22
22
  import "./chunk-6VDCH5ML.js";
23
23
  import {
@@ -154,4 +154,4 @@ export {
154
154
  startChatSession,
155
155
  stopSessionExecution
156
156
  };
157
- //# sourceMappingURL=chat-handler-MBR6ZQTX.js.map
157
+ //# sourceMappingURL=chat-handler-6DRGMDXZ.js.map
@@ -2,7 +2,7 @@ import {
2
2
  injectWorkflowKickoffIfNeeded,
3
3
  runAgentTurn,
4
4
  runChatTurn
5
- } from "./chunk-NY6IGGKZ.js";
5
+ } from "./chunk-ZHSCXD75.js";
6
6
  import {
7
7
  applyDynamicContext,
8
8
  checkAborted,
@@ -16,7 +16,7 @@ import {
16
16
  loadAllAgentsDefault,
17
17
  saveItemToDir,
18
18
  spawnShellProcess
19
- } from "./chunk-3JRYK5UG.js";
19
+ } from "./chunk-KX4C5Q5J.js";
20
20
  import {
21
21
  getPlatformShell,
22
22
  onProcessEvent
@@ -2102,4 +2102,4 @@ export {
2102
2102
  signalMcpReady,
2103
2103
  createWebSocketServer
2104
2104
  };
2105
- //# sourceMappingURL=chunk-DOJKA4XO.js.map
2105
+ //# sourceMappingURL=chunk-AA4G4DGK.js.map
@@ -34,7 +34,7 @@ import {
34
34
  deleteSetting,
35
35
  getSetting,
36
36
  setSetting
37
- } from "./chunk-WNSAJCWT.js";
37
+ } from "./chunk-SVSKQYFH.js";
38
38
  import {
39
39
  getProject
40
40
  } from "./chunk-YQ3SOPBI.js";
@@ -489,6 +489,13 @@ function cancelPathConfirmationsForSession(sessionId, reason) {
489
489
  }
490
490
 
491
491
  // src/server/tools/tool-helpers.ts
492
+ function buildSignal(timeoutMs, contextSignal) {
493
+ const timeoutSignal = AbortSignal.timeout(timeoutMs);
494
+ if (contextSignal) {
495
+ return AbortSignal.any([timeoutSignal, contextSignal]);
496
+ }
497
+ return timeoutSignal;
498
+ }
492
499
  function validateAction(action, allowed, startTime) {
493
500
  if (!action || !allowed.includes(action)) {
494
501
  return {
@@ -3651,7 +3658,7 @@ var callSubAgentTool = {
3651
3658
  };
3652
3659
  }
3653
3660
  try {
3654
- const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-WGAZKL56.js");
3661
+ const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-7QUMO5DJ.js");
3655
3662
  const toolRegistry = getToolRegistryForAgent2(agentDef);
3656
3663
  const turnMetrics = new TurnMetrics();
3657
3664
  const result = await executeSubAgent({
@@ -3790,13 +3797,6 @@ function buildAcceptHeader(format) {
3790
3797
  return "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
3791
3798
  }
3792
3799
  }
3793
- function buildSignal(timeoutMs, contextSignal) {
3794
- const timeoutSignal = AbortSignal.timeout(timeoutMs);
3795
- if (contextSignal) {
3796
- return AbortSignal.any([timeoutSignal, contextSignal]);
3797
- }
3798
- return timeoutSignal;
3799
- }
3800
3800
  function convertHTMLToMarkdown(html) {
3801
3801
  const turndown = new TurndownService({
3802
3802
  headingStyle: "atx",
@@ -4475,7 +4475,7 @@ function resolveAgentDef2(sessionManager, sessionId) {
4475
4475
  }
4476
4476
  async function buildCachedPrompt(sessionManager, sessionId, agentDef) {
4477
4477
  const { instructionContent, skills } = await loadSessionContext(sessionManager, sessionId);
4478
- const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-WGAZKL56.js");
4478
+ const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-7QUMO5DJ.js");
4479
4479
  const tools = getToolRegistryForAgent2(agentDef).definitions;
4480
4480
  const toolFingerprint = getToolFingerprint(tools);
4481
4481
  const allAgents = await loadAllAgentsDefault();
@@ -4488,7 +4488,7 @@ async function buildCachedPrompt(sessionManager, sessionId, agentDef) {
4488
4488
  async function computeSessionHash(sessionManager, sessionId) {
4489
4489
  const { instructionContent, skills } = await loadSessionContext(sessionManager, sessionId);
4490
4490
  const agentDef = await resolveAgentDef2(sessionManager, sessionId);
4491
- const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-WGAZKL56.js");
4491
+ const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-7QUMO5DJ.js");
4492
4492
  const tools = getToolRegistryForAgent2(agentDef).definitions;
4493
4493
  const toolFingerprint = getToolFingerprint(tools);
4494
4494
  return computeDynamicContextHash(instructionContent, skills, toolFingerprint);
@@ -4599,7 +4599,7 @@ var mcpConfigTool = createTool(
4599
4599
  await saveGlobalConfig(mcpConfigMode, { ...globalConfig, mcpServers: updated }, mcpConfigPath);
4600
4600
  }
4601
4601
  async function rebuildTools() {
4602
- const { setMcpTools: setMcpTools2 } = await import("./tools-WGAZKL56.js");
4602
+ const { setMcpTools: setMcpTools2 } = await import("./tools-7QUMO5DJ.js");
4603
4603
  const mcpTools = createMcpTools(mcpManagerForTools);
4604
4604
  setMcpTools2(mcpTools);
4605
4605
  }
@@ -4964,6 +4964,141 @@ var traceCodeTool = createTool(
4964
4964
  }
4965
4965
  );
4966
4966
 
4967
+ // src/server/tools/web-search.ts
4968
+ var DEFAULT_TIMEOUT_MS2 = 15e3;
4969
+ var DEFAULT_MAX_RESULTS = 5;
4970
+ function getTavilyApiKey() {
4971
+ return process.env["TAVILY_API_KEY"] ?? getSetting(SETTINGS_KEYS.SEARCH_TAVILY_API_KEY);
4972
+ }
4973
+ function getSearxngUrl() {
4974
+ return process.env["SEARXNG_URL"] ?? getSetting(SETTINGS_KEYS.SEARCH_SEARXNG_URL);
4975
+ }
4976
+ function getSearxngApiKey() {
4977
+ return process.env["SEARXNG_API_KEY"] ?? getSetting(SETTINGS_KEYS.SEARCH_SEARXNG_API_KEY);
4978
+ }
4979
+ function getActiveEngine() {
4980
+ const preferredEngine = getSetting(SETTINGS_KEYS.SEARCH_ENGINE);
4981
+ if (preferredEngine === "") {
4982
+ return null;
4983
+ }
4984
+ if (preferredEngine === "tavily") {
4985
+ const apiKey = getTavilyApiKey();
4986
+ if (!apiKey) return null;
4987
+ return { engine: "tavily", apiKey };
4988
+ }
4989
+ if (preferredEngine === "searxng") {
4990
+ const url = getSearxngUrl();
4991
+ if (!url) return null;
4992
+ const searxngApiKey = getSearxngApiKey();
4993
+ return searxngApiKey ? { engine: "searxng", url, apiKey: searxngApiKey } : { engine: "searxng", url };
4994
+ }
4995
+ const tavilyKey = getTavilyApiKey();
4996
+ if (tavilyKey) return { engine: "tavily", apiKey: tavilyKey };
4997
+ const searxngUrl = getSearxngUrl();
4998
+ if (searxngUrl) {
4999
+ const searxngApiKey = getSearxngApiKey();
5000
+ return searxngApiKey ? { engine: "searxng", url: searxngUrl, apiKey: searxngApiKey } : { engine: "searxng", url: searxngUrl };
5001
+ }
5002
+ return null;
5003
+ }
5004
+ async function searchTavily(query, maxResults, apiKey, signal) {
5005
+ const response = await fetch("https://api.tavily.com/search", {
5006
+ method: "POST",
5007
+ headers: { "Content-Type": "application/json" },
5008
+ body: JSON.stringify({ api_key: apiKey, query, max_results: maxResults }),
5009
+ signal
5010
+ });
5011
+ if (!response.ok) {
5012
+ const body = await response.text().catch(() => "");
5013
+ throw new Error(`Tavily search failed (${response.status}): ${body}`);
5014
+ }
5015
+ const data = await response.json();
5016
+ return (data.results ?? []).map((r) => ({ title: r.title, url: r.url, content: r.content }));
5017
+ }
5018
+ async function searchSearxng(query, maxResults, config, signal) {
5019
+ const baseUrl = config.url.replace(/\/+$/, "");
5020
+ const url = new URL(`${baseUrl}/search`);
5021
+ url.searchParams.set("format", "json");
5022
+ url.searchParams.set("q", query);
5023
+ const headers = {};
5024
+ if (config.apiKey) {
5025
+ headers["Authorization"] = `Bearer ${config.apiKey}`;
5026
+ }
5027
+ const response = await fetch(url.toString(), { headers, signal });
5028
+ if (!response.ok) {
5029
+ const body = await response.text().catch(() => "");
5030
+ throw new Error(`SearXNG search failed (${response.status}): ${body}`);
5031
+ }
5032
+ const data = await response.json();
5033
+ return (data.results ?? []).slice(0, maxResults).map((r) => ({
5034
+ title: r.title,
5035
+ url: r.url,
5036
+ content: r.content
5037
+ }));
5038
+ }
5039
+ function formatResults(results) {
5040
+ return results.map((r, i) => `[${i + 1}] ${r.title}
5041
+ URL: ${r.url}
5042
+ ${r.content}`).join("\n\n");
5043
+ }
5044
+ function clampMaxResults(maxResults) {
5045
+ return maxResults > 0 ? maxResults : DEFAULT_MAX_RESULTS;
5046
+ }
5047
+ var webSearchTool = createTool(
5048
+ "web_search",
5049
+ {
5050
+ type: "function",
5051
+ function: {
5052
+ name: "web_search",
5053
+ description: "Search the web using a configured search engine (Tavily or SearXNG). Returns a list of results with title, URL, and content snippet for each. Use this to find relevant web pages, then use web_fetch to retrieve full content.",
5054
+ parameters: {
5055
+ type: "object",
5056
+ properties: {
5057
+ query: {
5058
+ type: "string",
5059
+ description: "The search query"
5060
+ },
5061
+ max_results: {
5062
+ type: "number",
5063
+ description: "Maximum number of search results to return (default: 5)"
5064
+ }
5065
+ },
5066
+ required: ["query"]
5067
+ }
5068
+ }
5069
+ },
5070
+ async (args, context, helpers) => {
5071
+ const query = args.query;
5072
+ const maxResults = clampMaxResults(args.max_results ?? DEFAULT_MAX_RESULTS);
5073
+ const active = getActiveEngine();
5074
+ if (!active) {
5075
+ return helpers.error(
5076
+ "web_search requires at least one search engine configured. Set the TAVILY_API_KEY environment variable, or configure SearXNG via SEARXNG_URL (and optionally SEARXNG_API_KEY). You can also configure these in Settings > Advanced > Search Engine."
5077
+ );
5078
+ }
5079
+ const signal = buildSignal(DEFAULT_TIMEOUT_MS2, context.signal);
5080
+ try {
5081
+ let results;
5082
+ if (active.engine === "tavily") {
5083
+ results = await searchTavily(query, maxResults, active.apiKey, signal);
5084
+ } else {
5085
+ const searxngConfig = { url: active.url };
5086
+ if (active.apiKey) searxngConfig.apiKey = active.apiKey;
5087
+ results = await searchSearxng(query, maxResults, searxngConfig, signal);
5088
+ }
5089
+ if (results.length === 0) {
5090
+ return helpers.success("No search results found.", false);
5091
+ }
5092
+ return helpers.success(formatResults(results), false);
5093
+ } catch (error) {
5094
+ if (error instanceof DOMException && error.name === "AbortError") {
5095
+ return helpers.error("Search request timed out (15s). Try a more specific query.");
5096
+ }
5097
+ return helpers.error(error instanceof Error ? error.message : "Search request failed");
5098
+ }
5099
+ }
5100
+ );
5101
+
4967
5102
  // src/server/tools/index.ts
4968
5103
  var _builtInTools;
4969
5104
  function getBuiltInTools() {
@@ -4979,6 +5114,7 @@ function getBuiltInTools() {
4979
5114
  loadSkillTool,
4980
5115
  returnValueTool,
4981
5116
  webFetchTool,
5117
+ webSearchTool,
4982
5118
  devServerTool,
4983
5119
  stepDoneTool,
4984
5120
  backgroundProcessTool,
@@ -5274,4 +5410,4 @@ export {
5274
5410
  getToolRegistryForAgent,
5275
5411
  createToolRegistry
5276
5412
  };
5277
- //# sourceMappingURL=chunk-3JRYK5UG.js.map
5413
+ //# sourceMappingURL=chunk-KX4C5Q5J.js.map
@@ -21,7 +21,7 @@ import {
21
21
  tokenFromPassword,
22
22
  verifyPassword,
23
23
  workflowExists
24
- } from "./chunk-DOJKA4XO.js";
24
+ } from "./chunk-AA4G4DGK.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-3JRYK5UG.js";
69
+ } from "./chunk-KX4C5Q5J.js";
70
70
  import {
71
71
  getPathSeparator,
72
72
  isAbsolutePath
@@ -84,7 +84,7 @@ import {
84
84
  deleteSetting,
85
85
  getSetting,
86
86
  setSetting
87
- } from "./chunk-WNSAJCWT.js";
87
+ } from "./chunk-SVSKQYFH.js";
88
88
  import {
89
89
  getProject
90
90
  } from "./chunk-YQ3SOPBI.js";
@@ -4078,7 +4078,7 @@ import { Router as Router8 } from "express";
4078
4078
  import { spawn as spawn2 } from "child_process";
4079
4079
 
4080
4080
  // src/constants.ts
4081
- var VERSION = "2.0.60";
4081
+ var VERSION = "2.0.61";
4082
4082
 
4083
4083
  // src/server/routes/auto-update.ts
4084
4084
  var updateInProgress = false;
@@ -4283,7 +4283,7 @@ async function createServerHandle(config3) {
4283
4283
  setMcpTools(mcpTools);
4284
4284
  logger.info("MCP tools registered", { count: mcpTools.length });
4285
4285
  }
4286
- const { signalMcpReady } = await import("./server-LMH7ZH5X.js");
4286
+ const { signalMcpReady } = await import("./server-JDB2QVKP.js");
4287
4287
  signalMcpReady();
4288
4288
  });
4289
4289
  const app = express();
@@ -4476,7 +4476,7 @@ async function createServerHandle(config3) {
4476
4476
  app.get("/api/sessions/:id", async (req, res) => {
4477
4477
  const { getEventStore: getEventStore2 } = await import("./events-52XADT6Y.js");
4478
4478
  const { buildMessagesFromStoredEvents } = await import("./folding-2BGFLV5J.js");
4479
- const { getPendingQuestionsForSession } = await import("./tools-WGAZKL56.js");
4479
+ const { getPendingQuestionsForSession } = await import("./tools-7QUMO5DJ.js");
4480
4480
  const session = sessionManager.getSession(req.params.id);
4481
4481
  if (!session) {
4482
4482
  return res.status(404).json({ error: "Session not found" });
@@ -4495,8 +4495,8 @@ async function createServerHandle(config3) {
4495
4495
  if (!session) {
4496
4496
  return res.status(404).json({ error: "Session not found" });
4497
4497
  }
4498
- const { stopSessionExecution } = await import("./chat-handler-MBR6ZQTX.js");
4499
- const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-WGAZKL56.js");
4498
+ const { stopSessionExecution } = await import("./chat-handler-6DRGMDXZ.js");
4499
+ const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-7QUMO5DJ.js");
4500
4500
  sessionManager.clearMessageQueue(sessionId);
4501
4501
  stopSessionExecution(sessionId, sessionManager);
4502
4502
  abortSession(sessionId);
@@ -4650,7 +4650,7 @@ async function createServerHandle(config3) {
4650
4650
  if (!callId || approved === void 0) {
4651
4651
  return res.status(400).json({ error: "callId and approved are required" });
4652
4652
  }
4653
- const { providePathConfirmation } = await import("./tools-WGAZKL56.js");
4653
+ const { providePathConfirmation } = await import("./tools-7QUMO5DJ.js");
4654
4654
  const result = providePathConfirmation(callId, approved, alwaysAllow);
4655
4655
  if (!result.found) {
4656
4656
  return res.status(404).json({ error: "No pending path confirmation with that ID" });
@@ -4658,7 +4658,7 @@ async function createServerHandle(config3) {
4658
4658
  const { getEventStore: getEventStore2 } = await import("./events-52XADT6Y.js");
4659
4659
  const { buildMessagesFromStoredEvents, foldPendingConfirmations } = await import("./folding-2BGFLV5J.js");
4660
4660
  const { createSessionStateMessage } = await import("./protocol-CTNE3ANP.js");
4661
- const { getPendingQuestionsForSession } = await import("./tools-WGAZKL56.js");
4661
+ const { getPendingQuestionsForSession } = await import("./tools-7QUMO5DJ.js");
4662
4662
  const eventStore = getEventStore2();
4663
4663
  const events = eventStore.getEvents(sessionId);
4664
4664
  const messages = buildMessagesFromStoredEvents(events);
@@ -4679,7 +4679,7 @@ async function createServerHandle(config3) {
4679
4679
  if (!skip && typeof answer !== "string") {
4680
4680
  return res.status(400).json({ error: "answer is required when not skipping" });
4681
4681
  }
4682
- const { provideAnswer } = await import("./tools-WGAZKL56.js");
4682
+ const { provideAnswer } = await import("./tools-7QUMO5DJ.js");
4683
4683
  const found = provideAnswer(callId, answer ?? "", skip ?? false);
4684
4684
  if (!found) {
4685
4685
  return res.status(404).json({ error: "No pending question with that ID" });
@@ -4707,7 +4707,7 @@ async function createServerHandle(config3) {
4707
4707
  if (!session) {
4708
4708
  return res.status(404).json({ error: "Session not found" });
4709
4709
  }
4710
- const { getSetting: getSetting2, SETTINGS_KEYS } = await import("./settings-2TQA7VTP.js");
4710
+ const { getSetting: getSetting2, SETTINGS_KEYS } = await import("./settings-ASLTSUE2.js");
4711
4711
  if (getSetting2(SETTINGS_KEYS.CACHE_WARMING) !== "true") {
4712
4712
  return res.json({ success: false, reason: "disabled" });
4713
4713
  }
@@ -4739,7 +4739,7 @@ async function createServerHandle(config3) {
4739
4739
  backend: activeProvider?.backend ?? llmClient.getBackend(),
4740
4740
  model: llmClient.getModel()
4741
4741
  };
4742
- const { runAgentTurn, TurnMetrics } = await import("./orchestrator-PIZJ42J2.js");
4742
+ const { runAgentTurn, TurnMetrics } = await import("./orchestrator-AHCSLBB3.js");
4743
4743
  runAgentTurn(
4744
4744
  {
4745
4745
  sessionManager,
@@ -4777,8 +4777,8 @@ async function createServerHandle(config3) {
4777
4777
  if (!session) {
4778
4778
  return res.status(404).json({ error: "Session not found" });
4779
4779
  }
4780
- const { stopSessionExecution } = await import("./chat-handler-MBR6ZQTX.js");
4781
- const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-WGAZKL56.js");
4780
+ const { stopSessionExecution } = await import("./chat-handler-6DRGMDXZ.js");
4781
+ const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-7QUMO5DJ.js");
4782
4782
  const queuedMessages = sessionManager.getQueueState(sessionId);
4783
4783
  sessionManager.clearMessageQueue(sessionId);
4784
4784
  stopSessionExecution(sessionId, sessionManager);
@@ -4858,7 +4858,7 @@ async function createServerHandle(config3) {
4858
4858
  res.json({ accepted: true });
4859
4859
  });
4860
4860
  app.get("/api/settings", async (req, res) => {
4861
- const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-2TQA7VTP.js");
4861
+ const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-ASLTSUE2.js");
4862
4862
  const keysParam = req.query["keys"];
4863
4863
  if (!keysParam) {
4864
4864
  return res.status(400).json({ error: "keys query parameter is required" });
@@ -4871,19 +4871,19 @@ async function createServerHandle(config3) {
4871
4871
  res.json(result);
4872
4872
  });
4873
4873
  app.get("/api/settings/:key", async (req, res) => {
4874
- const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-2TQA7VTP.js");
4874
+ const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-ASLTSUE2.js");
4875
4875
  const key = req.params.key;
4876
4876
  const value = getSetting2(key) ?? SETTINGS_DEFAULTS[key] ?? null;
4877
4877
  res.json({ key, value });
4878
4878
  });
4879
4879
  app.get("/api/settings/:key", async (req, res) => {
4880
- const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-2TQA7VTP.js");
4880
+ const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-ASLTSUE2.js");
4881
4881
  const key = req.params.key;
4882
4882
  const value = getSetting2(key) ?? SETTINGS_DEFAULTS[key] ?? null;
4883
4883
  res.json({ key, value });
4884
4884
  });
4885
4885
  app.put("/api/settings/:key", async (req, res) => {
4886
- const { setSetting: setSetting2 } = await import("./settings-2TQA7VTP.js");
4886
+ const { setSetting: setSetting2 } = await import("./settings-ASLTSUE2.js");
4887
4887
  const key = req.params.key;
4888
4888
  const { value } = req.body;
4889
4889
  if (value === void 0) {
@@ -4991,6 +4991,56 @@ async function createServerHandle(config3) {
4991
4991
  });
4992
4992
  }
4993
4993
  });
4994
+ app.post("/api/search/test", async (req, res) => {
4995
+ const { engine, tavilyApiKey, searxngUrl, searxngApiKey } = req.body;
4996
+ try {
4997
+ if (engine === "tavily") {
4998
+ const key = tavilyApiKey || process.env["TAVILY_API_KEY"];
4999
+ if (!key) {
5000
+ return res.status(400).json({ success: false, error: "Tavily API key is required" });
5001
+ }
5002
+ const response = await fetch("https://api.tavily.com/search", {
5003
+ method: "POST",
5004
+ headers: { "Content-Type": "application/json" },
5005
+ body: JSON.stringify({ api_key: key, query: "test", max_results: 1 }),
5006
+ signal: AbortSignal.timeout(1e4)
5007
+ });
5008
+ if (!response.ok) {
5009
+ const body = await response.text().catch(() => "");
5010
+ return res.status(400).json({ success: false, error: `Tavily error (${response.status}): ${body}` });
5011
+ }
5012
+ return res.json({ success: true, message: "Tavily connection OK" });
5013
+ }
5014
+ if (engine === "searxng") {
5015
+ const url = searxngUrl || process.env["SEARXNG_URL"];
5016
+ if (!url) {
5017
+ return res.status(400).json({ success: false, error: "SearXNG URL is required" });
5018
+ }
5019
+ const searchUrl = new URL(`${url.replace(/\/+$/, "")}/search`);
5020
+ searchUrl.searchParams.set("format", "json");
5021
+ searchUrl.searchParams.set("q", "test");
5022
+ const apiKey = searxngApiKey || process.env["SEARXNG_API_KEY"] || void 0;
5023
+ const headers = {};
5024
+ if (apiKey) headers["Authorization"] = `Bearer ${apiKey}`;
5025
+ const response = await fetch(searchUrl.toString(), {
5026
+ headers,
5027
+ signal: AbortSignal.timeout(1e4)
5028
+ });
5029
+ if (!response.ok) {
5030
+ const body = await response.text().catch(() => "");
5031
+ return res.status(400).json({ success: false, error: `SearXNG error (${response.status}): ${body}` });
5032
+ }
5033
+ return res.json({ success: true, message: "SearXNG connection OK" });
5034
+ }
5035
+ return res.status(400).json({ success: false, error: "Invalid engine" });
5036
+ } catch (error) {
5037
+ const message = error instanceof Error ? error.message : "Connection failed";
5038
+ if (error instanceof DOMException && error.name === "AbortError") {
5039
+ return res.status(400).json({ success: false, error: "Connection timed out" });
5040
+ }
5041
+ return res.status(400).json({ success: false, error: message });
5042
+ }
5043
+ });
4994
5044
  app.get("/api/providers/models", async (req, res) => {
4995
5045
  const url = req.query["url"];
4996
5046
  const apiKey = req.query["apiKey"];
@@ -5293,7 +5343,7 @@ async function createServerHandle(config3) {
5293
5343
  });
5294
5344
  async function rebuildMcpTools() {
5295
5345
  const { createMcpTools: createMcpTools2 } = await import("./tool-adapter-MOGEI6TP.js");
5296
- const { setMcpTools: setMcpTools2 } = await import("./tools-WGAZKL56.js");
5346
+ const { setMcpTools: setMcpTools2 } = await import("./tools-7QUMO5DJ.js");
5297
5347
  const mcpTools = createMcpTools2(mcpManager);
5298
5348
  setMcpTools2(mcpTools);
5299
5349
  }
@@ -5613,7 +5663,7 @@ async function createServerHandle(config3) {
5613
5663
  const state = sessionManager.getContextState(sessionId);
5614
5664
  wssExports.broadcastForSession(sessionId, createContextStateMessage(state));
5615
5665
  });
5616
- const { QueueProcessor } = await import("./processor-PKKTNKWY.js");
5666
+ const { QueueProcessor } = await import("./processor-45VWLZIV.js");
5617
5667
  const queueProcessor = new QueueProcessor({
5618
5668
  sessionManager,
5619
5669
  providerManager,
@@ -5693,4 +5743,4 @@ export {
5693
5743
  createServerHandle,
5694
5744
  createServer
5695
5745
  };
5696
- //# sourceMappingURL=chunk-DEWNWF2P.js.map
5746
+ //# sourceMappingURL=chunk-LHTVTDQQ.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-GKS55Y2C.js");
211
+ const { runServe } = await import("./serve-V44O2QE6.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-MTBQQ5DN.js.map
223
+ //# sourceMappingURL=chunk-S5DWUGKZ.js.map
@@ -20,7 +20,11 @@ var SETTINGS_KEYS = {
20
20
  CACHE_WARMING: "cache.warming",
21
21
  KEYBINDINGS: "keybindings",
22
22
  RETRY_PATTERNS: "agent.retryPatterns",
23
- SKILLS_DIRECTORIES: "skills.directories"
23
+ SKILLS_DIRECTORIES: "skills.directories",
24
+ SEARCH_ENGINE: "search.engine",
25
+ SEARCH_TAVILY_API_KEY: "search.tavilyApiKey",
26
+ SEARCH_SEARXNG_URL: "search.searxngUrl",
27
+ SEARCH_SEARXNG_API_KEY: "search.searxngApiKey"
24
28
  };
25
29
  var SETTINGS_DEFAULTS = {
26
30
  [SETTINGS_KEYS.DISPLAY_SHOW_THINKING]: "true",
@@ -93,4 +97,4 @@ export {
93
97
  deleteSetting,
94
98
  getAllSettings
95
99
  };
96
- //# sourceMappingURL=chunk-WNSAJCWT.js.map
100
+ //# sourceMappingURL=chunk-SVSKQYFH.js.map
@@ -12,7 +12,7 @@ import {
12
12
  loadAllAgentsDefault,
13
13
  processEventsForConversation,
14
14
  runTopLevelAgentLoop
15
- } from "./chunk-3JRYK5UG.js";
15
+ } from "./chunk-KX4C5Q5J.js";
16
16
  import {
17
17
  TurnMetrics,
18
18
  WORKFLOW_KICKOFF_PROMPT,
@@ -39,7 +39,7 @@ import {
39
39
 
40
40
  // src/server/chat/orchestrator.ts
41
41
  async function buildRetryPatterns() {
42
- const { getSetting, SETTINGS_KEYS } = await import("./settings-2TQA7VTP.js");
42
+ const { getSetting, SETTINGS_KEYS } = await import("./settings-ASLTSUE2.js");
43
43
  const raw = getSetting(SETTINGS_KEYS.RETRY_PATTERNS);
44
44
  if (!raw) {
45
45
  const oldXmlProtection = getSetting("llm.disableXmlProtection");
@@ -324,4 +324,4 @@ export {
324
324
  runAgentTurn,
325
325
  injectWorkflowKickoffIfNeeded
326
326
  };
327
- //# sourceMappingURL=chunk-NY6IGGKZ.js.map
327
+ //# sourceMappingURL=chunk-ZHSCXD75.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-MTBQQ5DN.js";
4
+ } from "../chunk-S5DWUGKZ.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-MTBQQ5DN.js";
4
+ } from "../chunk-S5DWUGKZ.js";
5
5
  import "../chunk-XY3LESVZ.js";
6
6
  import {
7
7
  logger
@@ -2,15 +2,15 @@ import {
2
2
  injectWorkflowKickoffIfNeeded,
3
3
  runAgentTurn,
4
4
  runChatTurn
5
- } from "./chunk-NY6IGGKZ.js";
6
- import "./chunk-3JRYK5UG.js";
5
+ } from "./chunk-ZHSCXD75.js";
6
+ import "./chunk-KX4C5Q5J.js";
7
7
  import "./chunk-LBGHZLLH.js";
8
8
  import "./chunk-BGTEIA2S.js";
9
9
  import "./chunk-PK2RMVMB.js";
10
10
  import "./chunk-NWO6GRYE.js";
11
11
  import "./chunk-F4PMNP7S.js";
12
12
  import "./chunk-EU3WWTFH.js";
13
- import "./chunk-WNSAJCWT.js";
13
+ import "./chunk-SVSKQYFH.js";
14
14
  import "./chunk-YQ3SOPBI.js";
15
15
  import {
16
16
  TurnMetrics,
@@ -43,4 +43,4 @@ export {
43
43
  runAgentTurn,
44
44
  runChatTurn
45
45
  };
46
- //# sourceMappingURL=orchestrator-PIZJ42J2.js.map
46
+ //# sourceMappingURL=orchestrator-AHCSLBB3.js.map
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openfox",
3
- "version": "2.0.60",
3
+ "version": "2.0.61",
4
4
  "description": "Local-LLM-first agentic coding assistant",
5
5
  "type": "module",
6
6
  "bin": {
@@ -3,7 +3,7 @@ import {
3
3
  finalizeTurnCompletion,
4
4
  generateSessionNameForSession
5
5
  } from "./chunk-NVPVDDQZ.js";
6
- import "./chunk-3JRYK5UG.js";
6
+ import "./chunk-KX4C5Q5J.js";
7
7
  import "./chunk-LBGHZLLH.js";
8
8
  import "./chunk-BGTEIA2S.js";
9
9
  import "./chunk-PK2RMVMB.js";
@@ -13,7 +13,7 @@ import {
13
13
  createSessionRunningMessage
14
14
  } from "./chunk-F4PMNP7S.js";
15
15
  import "./chunk-EU3WWTFH.js";
16
- import "./chunk-WNSAJCWT.js";
16
+ import "./chunk-SVSKQYFH.js";
17
17
  import "./chunk-YQ3SOPBI.js";
18
18
  import "./chunk-6VDCH5ML.js";
19
19
  import {
@@ -174,7 +174,7 @@ var QueueProcessor = class {
174
174
  backend: provider?.backend ?? llmClient.getBackend(),
175
175
  model: llmClient.getModel()
176
176
  };
177
- const { runChatTurn } = await import("./orchestrator-PIZJ42J2.js");
177
+ const { runChatTurn } = await import("./orchestrator-AHCSLBB3.js");
178
178
  const runChatTurnParams = buildRunChatTurnParams({
179
179
  sessionManager,
180
180
  sessionId,
@@ -222,4 +222,4 @@ var QueueProcessor = class {
222
222
  export {
223
223
  QueueProcessor
224
224
  };
225
- //# sourceMappingURL=processor-PKKTNKWY.js.map
225
+ //# sourceMappingURL=processor-45VWLZIV.js.map