openfox 2.0.38 → 2.0.40

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 (29) hide show
  1. package/dist/agent-defaults/builder.agent.md +2 -0
  2. package/dist/agent-defaults/code-reviewer.agent.md +1 -0
  3. package/dist/agent-defaults/explorer.agent.md +1 -0
  4. package/dist/agent-defaults/planner.agent.md +1 -0
  5. package/dist/{chat-handler-TPKAEATB.js → chat-handler-KPQ3OM3U.js} +4 -4
  6. package/dist/{chunk-7W2WATAO.js → chunk-34CRQ76B.js} +2 -2
  7. package/dist/{chunk-GZZW4XSI.js → chunk-EZJFNX7D.js} +3 -3
  8. package/dist/{chunk-ATBNY4CL.js → chunk-J7QZK6WE.js} +316 -28
  9. package/dist/{chunk-DL6ZILAF.js → chunk-LBGHZLLH.js} +63 -16
  10. package/dist/{chunk-HJ7KRYWX.js → chunk-LH5J7V4F.js} +273 -8
  11. package/dist/{chunk-FFQCZ2MI.js → chunk-WUG3TOAL.js} +2 -2
  12. package/dist/cli/dev.js +1 -1
  13. package/dist/cli/index.js +1 -1
  14. package/dist/{inspect-proxy-42ZXL2R5.js → inspect-proxy-UT2LFGJ5.js} +4 -2
  15. package/dist/{orchestrator-ZFBVVQBO.js → orchestrator-O4FZ3UVV.js} +4 -4
  16. package/dist/package.json +1 -1
  17. package/dist/{processor-NG4NH2SS.js → processor-PQ5IQ24D.js} +4 -4
  18. package/dist/{serve-VIWHYE6X.js → serve-NC3U2VTG.js} +6 -6
  19. package/dist/server/index.d.ts +88 -0
  20. package/dist/server/index.js +5 -5
  21. package/dist/server/public/.gitkeep +0 -0
  22. package/dist/{server-MEJPUI4S.js → server-INICKQAY.js} +5 -5
  23. package/dist/{tools-ATO5MSWX.js → tools-Q32G4RCJ.js} +3 -3
  24. package/dist/web/__inspect__.js +81 -34
  25. package/dist/web/assets/{index-I3ZLVKAn.js → index-chkieEXf.js} +28 -28
  26. package/dist/web/index.html +1 -1
  27. package/dist/web/sw.js +1 -1
  28. package/package.json +1 -1
  29. package/dist/server/public/__inspect__.js +0 -282
@@ -1,3 +1,6 @@
1
+ import {
2
+ getProjectByWorkdir
3
+ } from "./chunk-YQ3SOPBI.js";
1
4
  import {
2
5
  logger
3
6
  } from "./chunk-K44MW7JJ.js";
@@ -65,25 +68,24 @@ ${headerLines}\r
65
68
  return Buffer.concat([head, body]);
66
69
  }
67
70
  function dechunk(buf) {
68
- const str = buf.toString("utf8");
69
71
  const parts = [];
70
72
  let pos = 0;
71
- while (pos < str.length) {
72
- const nlIdx = str.indexOf("\r\n", pos);
73
+ while (pos < buf.length) {
74
+ const nlIdx = buf.indexOf("\r\n", pos);
73
75
  if (nlIdx < 0) break;
74
- const sizeStr = str.slice(pos, nlIdx);
76
+ const sizeStr = buf.subarray(pos, nlIdx).toString("ascii");
75
77
  const size = parseInt(sizeStr, 16);
76
78
  if (isNaN(size) || size < 0) break;
77
79
  if (size === 0) break;
78
80
  const chunkStart = nlIdx + 2;
79
81
  const chunkEnd = chunkStart + size;
80
- if (chunkEnd > str.length) break;
81
- parts.push(str.slice(chunkStart, chunkEnd));
82
+ if (chunkEnd > buf.length) break;
83
+ parts.push(buf.subarray(chunkStart, chunkEnd));
82
84
  pos = chunkEnd + 2;
83
85
  }
84
- return Buffer.from(parts.join(""), "utf8");
86
+ return Buffer.concat(parts);
85
87
  }
86
- function startInspectProxy(target, sessionManager) {
88
+ function startInspectProxy(target, sessionManager, workdir) {
87
89
  const port = getAvailablePort();
88
90
  const server = net.createServer((client) => {
89
91
  let clientHead = "";
@@ -100,6 +102,26 @@ function startInspectProxy(target, sessionManager) {
100
102
  const { method, url, headers } = parseReqHeaders(clientHead);
101
103
  const isWS = headers["upgrade"] === "websocket";
102
104
  clientParsed = true;
105
+ if (url === "/__openfox_sessions" && method === "GET") {
106
+ let sessions = sessionManager.listSessions();
107
+ if (workdir) {
108
+ const project = getProjectByWorkdir(workdir);
109
+ if (project) {
110
+ sessions = sessions.filter((s) => s.projectId === project.id);
111
+ }
112
+ }
113
+ sessions.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
114
+ const mapped = sessions.map((s) => ({
115
+ id: s.id,
116
+ title: s.title ?? s.id,
117
+ createdAt: s.createdAt
118
+ }));
119
+ client.write(
120
+ buildResponse(200, { "Content-Type": "application/json" }, Buffer.from(JSON.stringify({ sessions: mapped })))
121
+ );
122
+ client.end();
123
+ return;
124
+ }
103
125
  if (url === "/__openfox_feedback" && method === "POST") {
104
126
  const contentLength = parseInt(headers["content-length"] || "0", 10);
105
127
  let bodyData = chunk.slice(he + 4);
@@ -158,8 +180,10 @@ ${annotation || "(none)"}`;
158
180
  return;
159
181
  }
160
182
  targetSocket = net.connect(targetPort, targetHost);
161
- targetSocket.on("error", () => client.destroy());
162
- client.on("error", () => targetSocket.destroy());
183
+ targetSocket.on("error", () => {
184
+ respondWithError(502, "Bad Gateway");
185
+ });
186
+ client.on("error", () => targetSocket?.destroy());
163
187
  client.on("end", () => targetSocket.end());
164
188
  const connClose = "\r\nConnection: close";
165
189
  const reqEnd = clientHead.indexOf("\r\n\r\n");
@@ -176,7 +200,15 @@ ${annotation || "(none)"}`;
176
200
  let resHeaders = {};
177
201
  const bodyBuf = [];
178
202
  let headEnd = -1;
203
+ let responded = false;
204
+ function respondWithError(status2, message) {
205
+ if (responded) return;
206
+ responded = true;
207
+ client.write(buildResponse(status2, { "Content-Type": "text/plain" }, Buffer.from(message)));
208
+ client.end();
209
+ }
179
210
  targetSocket.on("data", (sChunk) => {
211
+ if (responded) return;
180
212
  if (!serverParsed) {
181
213
  serverHeadBuf += sChunk.toString("utf8");
182
214
  const sHe = serverHeadBuf.indexOf("\r\n\r\n");
@@ -215,17 +247,20 @@ ${annotation || "(none)"}`;
215
247
  client.write(sChunk);
216
248
  });
217
249
  targetSocket.on("end", () => {
250
+ if (responded) return;
218
251
  if (!serverParsed) {
219
- client.end();
252
+ respondWithError(502, "Upstream connection closed prematurely");
220
253
  return;
221
254
  }
222
255
  if (isHtml && enc) {
223
256
  const fullBody = Buffer.concat(bodyBuf);
257
+ const isChunked = (resHeaders["transfer-encoding"] || "").toLowerCase() === "chunked";
258
+ const decoded = isChunked ? dechunk(fullBody) : fullBody;
224
259
  let text;
225
260
  try {
226
- if (enc === "gzip") text = zlib.gunzipSync(fullBody).toString("utf8");
227
- else if (enc === "deflate") text = zlib.inflateSync(fullBody).toString("utf8");
228
- else text = fullBody.toString("utf8");
261
+ if (enc === "gzip") text = zlib.gunzipSync(decoded).toString("utf8");
262
+ else if (enc === "deflate") text = zlib.inflateSync(decoded).toString("utf8");
263
+ else text = decoded.toString("utf8");
229
264
  } catch {
230
265
  client.end();
231
266
  return;
@@ -236,6 +271,9 @@ ${annotation || "(none)"}`;
236
271
  if (bi >= 0) modified = text.slice(0, bi) + INJECT_SCRIPT + text.slice(bi);
237
272
  else if (hi >= 0) modified = text.slice(0, hi) + INJECT_SCRIPT + text.slice(hi);
238
273
  else {
274
+ const headStr2 = buildHttpHeaders(resHeaders, status);
275
+ client.write(Buffer.from(headStr2, "utf8"));
276
+ client.write(fullBody);
239
277
  client.end();
240
278
  return;
241
279
  }
@@ -246,6 +284,7 @@ ${annotation || "(none)"}`;
246
284
  const headStr = buildHttpHeaders(resHeaders, status);
247
285
  client.write(Buffer.from(headStr, "utf8"));
248
286
  client.write(compressed);
287
+ client.end();
249
288
  } else if (isHtml && bodyBuf.length > 0) {
250
289
  const fullBody = Buffer.concat(bodyBuf);
251
290
  const isChunked = (resHeaders["transfer-encoding"] || "").toLowerCase() === "chunked";
@@ -258,14 +297,22 @@ ${annotation || "(none)"}`;
258
297
  else if (hi >= 0)
259
298
  modified = Buffer.concat([dechunks.slice(0, hi), Buffer.from(INJECT_SCRIPT, "utf8"), dechunks.slice(hi)]);
260
299
  else {
300
+ const headStr2 = buildHttpHeaders(
301
+ resHeaders,
302
+ status,
303
+ Buffer.byteLength(dechunks),
304
+ "text/html; charset=utf-8"
305
+ );
306
+ client.write(Buffer.from(headStr2, "utf8"));
307
+ client.write(dechunks);
261
308
  client.end();
262
309
  return;
263
310
  }
264
311
  const headStr = buildHttpHeaders(resHeaders, status, Buffer.byteLength(modified), "text/html; charset=utf-8");
265
312
  client.write(Buffer.from(headStr, "utf8"));
266
313
  client.write(modified);
314
+ client.end();
267
315
  }
268
- client.end();
269
316
  });
270
317
  });
271
318
  client.on("error", () => {
@@ -293,4 +340,4 @@ export {
293
340
  startInspectProxy,
294
341
  stopAllInspectProxies
295
342
  };
296
- //# sourceMappingURL=chunk-DL6ZILAF.js.map
343
+ //# sourceMappingURL=chunk-LBGHZLLH.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  startInspectProxy
3
- } from "./chunk-DL6ZILAF.js";
3
+ } from "./chunk-LBGHZLLH.js";
4
4
  import {
5
5
  createProcess,
6
6
  getPlatformShell,
@@ -3452,7 +3452,7 @@ var callSubAgentTool = {
3452
3452
  };
3453
3453
  }
3454
3454
  try {
3455
- const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-ATO5MSWX.js");
3455
+ const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-Q32G4RCJ.js");
3456
3456
  const toolRegistry = getToolRegistryForAgent2(agentDef);
3457
3457
  const turnMetrics = new TurnMetrics();
3458
3458
  const result = await executeSubAgent({
@@ -3806,7 +3806,7 @@ var DevServerManager = class {
3806
3806
  instance.exited = false;
3807
3807
  if (!config.disableInspect && config.url && this._sessionManager) {
3808
3808
  try {
3809
- const { port, cleanup } = startInspectProxy(config.url, this._sessionManager);
3809
+ const { port, cleanup } = startInspectProxy(config.url, this._sessionManager, workdir);
3810
3810
  instance.inspectProxyPort = port;
3811
3811
  instance.proxyCleanup = cleanup;
3812
3812
  logger.debug("Inspect proxy started", { workdir, port, target: config.url });
@@ -4268,7 +4268,7 @@ function resolveAgentDef2(sessionManager, sessionId) {
4268
4268
  }
4269
4269
  async function buildCachedPrompt(sessionManager, sessionId, agentDef) {
4270
4270
  const { instructionContent, skills } = await loadSessionContext(sessionManager, sessionId);
4271
- const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-ATO5MSWX.js");
4271
+ const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-Q32G4RCJ.js");
4272
4272
  const tools = getToolRegistryForAgent2(agentDef).definitions;
4273
4273
  const toolFingerprint = getToolFingerprint(tools);
4274
4274
  const allAgents = await loadAllAgentsDefault();
@@ -4281,7 +4281,7 @@ async function buildCachedPrompt(sessionManager, sessionId, agentDef) {
4281
4281
  async function computeSessionHash(sessionManager, sessionId) {
4282
4282
  const { instructionContent, skills } = await loadSessionContext(sessionManager, sessionId);
4283
4283
  const agentDef = await resolveAgentDef2(sessionManager, sessionId);
4284
- const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-ATO5MSWX.js");
4284
+ const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-Q32G4RCJ.js");
4285
4285
  const tools = getToolRegistryForAgent2(agentDef).definitions;
4286
4286
  const toolFingerprint = getToolFingerprint(tools);
4287
4287
  return computeDynamicContextHash(instructionContent, skills, toolFingerprint);
@@ -4388,7 +4388,7 @@ var mcpConfigTool = createTool(
4388
4388
  await saveGlobalConfig(mcpConfigMode, { ...globalConfig, mcpServers: updated });
4389
4389
  }
4390
4390
  async function rebuildTools() {
4391
- const { setMcpTools: setMcpTools2 } = await import("./tools-ATO5MSWX.js");
4391
+ const { setMcpTools: setMcpTools2 } = await import("./tools-Q32G4RCJ.js");
4392
4392
  const mcpTools = createMcpTools(mcpManagerForTools);
4393
4393
  setMcpTools2(mcpTools);
4394
4394
  }
@@ -4489,6 +4489,270 @@ var mcpConfigTool = createTool(
4489
4489
  }
4490
4490
  );
4491
4491
 
4492
+ // src/server/tools/trace-code.ts
4493
+ import { stat as stat2, readFile as readFile8 } from "fs/promises";
4494
+ function graphNode(location, depth, relation, symbolName, symbolKind) {
4495
+ const node = { location, depth, relation, symbolName };
4496
+ if (symbolKind) node.symbolKind = symbolKind;
4497
+ return node;
4498
+ }
4499
+ var MAX_DEPTH = 5;
4500
+ var VALID_DIRECTIONS = ["up", "down", "both"];
4501
+ var SNIPPET_RADIUS = 2;
4502
+ var CACHE_MAX_SIZE = 50;
4503
+ function locationKey(loc) {
4504
+ return `${loc.path}:${loc.line}:${loc.character}`;
4505
+ }
4506
+ function formatLocation(loc, workdir) {
4507
+ const relPath = loc.path.startsWith(workdir) ? loc.path.slice(workdir.length + 1) : loc.path;
4508
+ return `${relPath}:${loc.line + 1}:${loc.character}`;
4509
+ }
4510
+ var fileCache = /* @__PURE__ */ new Map();
4511
+ async function getCachedLines(path, approvedPaths) {
4512
+ if (!approvedPaths.has(path)) return [];
4513
+ const cached = fileCache.get(path);
4514
+ if (cached) {
4515
+ try {
4516
+ const stats = await stat2(path);
4517
+ if (stats.mtimeMs === cached.mtimeMs) {
4518
+ return cached.lines;
4519
+ }
4520
+ } catch {
4521
+ }
4522
+ }
4523
+ try {
4524
+ const stats = await stat2(path);
4525
+ const content = await readFile8(path, "utf-8");
4526
+ const lines = content.split("\n");
4527
+ fileCache.set(path, { mtimeMs: stats.mtimeMs, lines });
4528
+ if (fileCache.size > CACHE_MAX_SIZE) {
4529
+ const oldest = fileCache.keys().next();
4530
+ if (!oldest.done && oldest.value !== void 0) {
4531
+ fileCache.delete(oldest.value);
4532
+ }
4533
+ }
4534
+ return lines;
4535
+ } catch {
4536
+ return [];
4537
+ }
4538
+ }
4539
+ async function getSnippet(path, line, approvedPaths, radius = SNIPPET_RADIUS) {
4540
+ const lines = await getCachedLines(path, approvedPaths);
4541
+ if (lines.length === 0) return "";
4542
+ const start = Math.max(0, line - radius);
4543
+ const end = Math.min(lines.length - 1, line + radius);
4544
+ const lineNumWidth = String(end + 1).length;
4545
+ const result = [];
4546
+ for (let i = start; i <= end; i++) {
4547
+ const gutter = i === line ? ">" : " ";
4548
+ const lineNum = String(i + 1).padStart(lineNumWidth);
4549
+ result.push(`${gutter} ${lineNum}\u2502 ${lines[i] ?? ""}`);
4550
+ }
4551
+ return result.join("\n");
4552
+ }
4553
+ function formatEdgeLabel(relation) {
4554
+ switch (relation) {
4555
+ case "definition":
4556
+ return "definition";
4557
+ case "references":
4558
+ return "reference";
4559
+ case "type-definition":
4560
+ return "type definition";
4561
+ default:
4562
+ return relation;
4563
+ }
4564
+ }
4565
+ async function collectNodes(lsp, startLocations, symbolName, symbolKind, maxDepth, direction) {
4566
+ const visited = /* @__PURE__ */ new Set();
4567
+ const nodes = [];
4568
+ const edges = [];
4569
+ const queue = startLocations.map((loc) => ({ location: loc, depth: 0, relation: "match" }));
4570
+ let idx = 0;
4571
+ while (idx < queue.length) {
4572
+ const item = queue[idx];
4573
+ idx++;
4574
+ const key = locationKey(item.location);
4575
+ if (visited.has(key)) continue;
4576
+ visited.add(key);
4577
+ nodes.push(graphNode(item.location, item.depth, item.relation, symbolName, symbolKind));
4578
+ if (item.parentKey) {
4579
+ const relation = item.relation === "definition" ? "definition" : item.relation === "type-definition" ? "type-definition" : "references";
4580
+ edges.push({ from: item.parentKey, to: key, relation });
4581
+ }
4582
+ if (item.depth >= maxDepth) continue;
4583
+ const { path, line, character } = item.location;
4584
+ if (direction === "down" || direction === "both") {
4585
+ const defs = await lsp.getDefinition(path, line, character);
4586
+ for (const def of defs) {
4587
+ const defKey = locationKey(def);
4588
+ if (!visited.has(defKey)) {
4589
+ queue.push({ location: def, depth: item.depth + 1, relation: "definition", parentKey: key });
4590
+ }
4591
+ }
4592
+ const typeDefs = await lsp.getTypeDefinition(path, line, character);
4593
+ for (const td of typeDefs) {
4594
+ const tdKey = locationKey(td);
4595
+ if (!visited.has(tdKey)) {
4596
+ queue.push({ location: td, depth: item.depth + 1, relation: "type-definition", parentKey: key });
4597
+ }
4598
+ }
4599
+ }
4600
+ if (direction === "up" || direction === "both") {
4601
+ const refs = await lsp.getReferences(path, line, character);
4602
+ for (const ref of refs) {
4603
+ const refKey = locationKey(ref);
4604
+ if (!visited.has(refKey)) {
4605
+ queue.push({ location: ref, depth: item.depth + 1, relation: "reference", parentKey: key });
4606
+ }
4607
+ }
4608
+ }
4609
+ }
4610
+ return { nodes, edges };
4611
+ }
4612
+ async function formatOutput(symbolName, direction, depth, nodes, edges, workdir, approvedPaths) {
4613
+ const lines = [];
4614
+ lines.push(`Symbol: ${symbolName}`);
4615
+ lines.push(`Direction: ${direction} | Depth: ${depth}`);
4616
+ lines.push(`Nodes: ${nodes.length} | Edges: ${edges.length}`);
4617
+ lines.push("");
4618
+ if (nodes.length === 0) {
4619
+ lines.push("No results found.");
4620
+ return lines.join("\n");
4621
+ }
4622
+ const byDepth = /* @__PURE__ */ new Map();
4623
+ for (const node of nodes) {
4624
+ const group = byDepth.get(node.depth) ?? [];
4625
+ group.push(node);
4626
+ byDepth.set(node.depth, group);
4627
+ }
4628
+ for (const [depth2, group] of [...byDepth.entries()].sort(([a], [b]) => a - b)) {
4629
+ lines.push(`\u2500\u2500 Depth ${depth2} \u2500\u2500`);
4630
+ const fileGroups = /* @__PURE__ */ new Map();
4631
+ for (const node of group) {
4632
+ const fileKey = node.location.path;
4633
+ const fileGroup = fileGroups.get(fileKey) ?? [];
4634
+ fileGroup.push(node);
4635
+ fileGroups.set(fileKey, fileGroup);
4636
+ }
4637
+ for (const [filePath, fileNodes] of fileGroups) {
4638
+ const relPath = filePath.startsWith(workdir) ? filePath.slice(workdir.length + 1) : filePath;
4639
+ if (fileNodes.length > 5) {
4640
+ const kinds = [...new Set(fileNodes.map((n) => formatEdgeLabel(n.relation)))];
4641
+ lines.push(` ${kinds.join(", ")} \xD7 ${fileNodes.length} in ${relPath}`);
4642
+ } else {
4643
+ for (const node of fileNodes) {
4644
+ const label = formatEdgeLabel(node.relation);
4645
+ const kind = node.symbolKind ? ` (${node.symbolKind})` : "";
4646
+ const loc = formatLocation(node.location, workdir);
4647
+ lines.push(` ${label}: ${loc}${kind}`);
4648
+ const snippet = await getSnippet(node.location.path, node.location.line, approvedPaths);
4649
+ if (snippet) {
4650
+ for (const snipLine of snippet.split("\n")) {
4651
+ lines.push(` ${snipLine}`);
4652
+ }
4653
+ }
4654
+ }
4655
+ }
4656
+ }
4657
+ lines.push("");
4658
+ }
4659
+ if (edges.length > 0 && edges.length <= 20) {
4660
+ lines.push("Edges:");
4661
+ for (const edge of edges) {
4662
+ lines.push(` ${edge.from} \u2500\u2500${edge.relation}\u2500\u2500\u25B6 ${edge.to}`);
4663
+ }
4664
+ lines.push("");
4665
+ } else if (edges.length > 20) {
4666
+ lines.push(`Edges: ${edges.length} total (suppressed, too many to display individually)`);
4667
+ lines.push("");
4668
+ }
4669
+ return lines.join("\n");
4670
+ }
4671
+ var traceCodeTool = createTool(
4672
+ "trace_code",
4673
+ {
4674
+ type: "function",
4675
+ function: {
4676
+ name: "trace_code",
4677
+ description: "Trace a symbol through the codebase using LSP-powered static analysis. Finds definitions, references, and type definitions up to a configurable depth. Returns a graph of locations with inline code snippets for each node.",
4678
+ parameters: {
4679
+ type: "object",
4680
+ properties: {
4681
+ symbol: {
4682
+ type: "string",
4683
+ description: 'Symbol name to trace (e.g., "rebuildTools", "handleSubmit", "UserProfile")'
4684
+ },
4685
+ file: {
4686
+ type: "string",
4687
+ description: "File path containing the symbol. Used to seed the LSP server and detect the language. Relative to the working directory."
4688
+ },
4689
+ depth: {
4690
+ type: "number",
4691
+ description: "Graph traversal depth (default: 1, max: 5). How many hops to follow. Start with 1 for immediate defs+refs.",
4692
+ default: 1
4693
+ },
4694
+ direction: {
4695
+ type: "string",
4696
+ enum: ["up", "down", "both"],
4697
+ description: '"down" follows definitions, "up" finds references, "both" does both (default).',
4698
+ default: "both"
4699
+ }
4700
+ },
4701
+ required: ["symbol", "file"]
4702
+ }
4703
+ }
4704
+ },
4705
+ async (args, context, helpers) => {
4706
+ const symbol = args.symbol?.trim();
4707
+ if (!symbol) {
4708
+ return helpers.error("symbol is required");
4709
+ }
4710
+ const file = args.file?.trim();
4711
+ if (!file) {
4712
+ return helpers.error("file is required");
4713
+ }
4714
+ const depth = args.depth ?? 1;
4715
+ if (depth > MAX_DEPTH) {
4716
+ return helpers.error(`depth cannot exceed ${MAX_DEPTH}`);
4717
+ }
4718
+ const direction = args.direction ?? "both";
4719
+ if (!VALID_DIRECTIONS.includes(direction)) {
4720
+ return helpers.error(`direction must be one of: ${VALID_DIRECTIONS.join(", ")}`);
4721
+ }
4722
+ const lsp = context.lspManager;
4723
+ if (!lsp) {
4724
+ return helpers.error("LSP is not available. The trace_code tool requires a running LSP server.");
4725
+ }
4726
+ const fullPath = helpers.resolvePath(file);
4727
+ try {
4728
+ await stat2(fullPath);
4729
+ } catch {
4730
+ return helpers.error(`File not found: "${file}". Check that the path exists.`);
4731
+ }
4732
+ let symbols;
4733
+ try {
4734
+ symbols = await lsp.seedAndFindWorkspaceSymbol(symbol, fullPath);
4735
+ } catch {
4736
+ return helpers.error(`Failed to search for symbol "${symbol}". LSP may not be responding.`);
4737
+ }
4738
+ if (symbols.length === 0) {
4739
+ const installHint = lsp.getInstallHint(fullPath);
4740
+ const hint = installHint ? ` ${installHint}` : "";
4741
+ return helpers.error(
4742
+ `Symbol "${symbol}" not found in "${file}". Check that the symbol name is correct and that the LSP server has indexed the project.${hint}`
4743
+ );
4744
+ }
4745
+ const startLocations = symbols.map((s) => s.location);
4746
+ const symbolKind = symbols[0].kind;
4747
+ const { nodes, edges } = await collectNodes(lsp, startLocations, symbol, symbolKind, depth, direction);
4748
+ const allPaths = [...new Set(nodes.map((n) => n.location.path))];
4749
+ await helpers.checkPathAccess(allPaths);
4750
+ const approvedPaths = new Set(allPaths);
4751
+ const output = await formatOutput(symbol, direction, depth, nodes, edges, context.workdir, approvedPaths);
4752
+ return helpers.success(output);
4753
+ }
4754
+ );
4755
+
4492
4756
  // src/server/tools/index.ts
4493
4757
  var _builtInTools;
4494
4758
  function getBuiltInTools() {
@@ -4507,7 +4771,8 @@ function getBuiltInTools() {
4507
4771
  devServerTool,
4508
4772
  stepDoneTool,
4509
4773
  backgroundProcessTool,
4510
- mcpConfigTool
4774
+ mcpConfigTool,
4775
+ traceCodeTool
4511
4776
  ];
4512
4777
  }
4513
4778
  return _builtInTools;
@@ -4793,4 +5058,4 @@ export {
4793
5058
  getToolRegistryForAgent,
4794
5059
  createToolRegistry
4795
5060
  };
4796
- //# sourceMappingURL=chunk-HJ7KRYWX.js.map
5061
+ //# sourceMappingURL=chunk-LH5J7V4F.js.map
@@ -12,7 +12,7 @@ import {
12
12
  loadAllAgentsDefault,
13
13
  processEventsForConversation,
14
14
  runTopLevelAgentLoop
15
- } from "./chunk-HJ7KRYWX.js";
15
+ } from "./chunk-LH5J7V4F.js";
16
16
  import {
17
17
  TurnMetrics,
18
18
  WORKFLOW_KICKOFF_PROMPT,
@@ -310,4 +310,4 @@ export {
310
310
  runAgentTurn,
311
311
  injectWorkflowKickoffIfNeeded
312
312
  };
313
- //# sourceMappingURL=chunk-FFQCZ2MI.js.map
313
+ //# sourceMappingURL=chunk-WUG3TOAL.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-7W2WATAO.js";
4
+ } from "../chunk-34CRQ76B.js";
5
5
  import {
6
6
  logger
7
7
  } from "../chunk-K44MW7JJ.js";
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-7W2WATAO.js";
4
+ } from "../chunk-34CRQ76B.js";
5
5
  import {
6
6
  logger
7
7
  } from "../chunk-K44MW7JJ.js";
@@ -1,10 +1,12 @@
1
1
  import {
2
2
  startInspectProxy,
3
3
  stopAllInspectProxies
4
- } from "./chunk-DL6ZILAF.js";
4
+ } from "./chunk-LBGHZLLH.js";
5
+ import "./chunk-YQ3SOPBI.js";
6
+ import "./chunk-ZPIZLTWU.js";
5
7
  import "./chunk-K44MW7JJ.js";
6
8
  export {
7
9
  startInspectProxy,
8
10
  stopAllInspectProxies
9
11
  };
10
- //# sourceMappingURL=inspect-proxy-42ZXL2R5.js.map
12
+ //# sourceMappingURL=inspect-proxy-UT2LFGJ5.js.map
@@ -2,9 +2,9 @@ import {
2
2
  injectWorkflowKickoffIfNeeded,
3
3
  runAgentTurn,
4
4
  runChatTurn
5
- } from "./chunk-FFQCZ2MI.js";
6
- import "./chunk-HJ7KRYWX.js";
7
- import "./chunk-DL6ZILAF.js";
5
+ } from "./chunk-WUG3TOAL.js";
6
+ import "./chunk-LH5J7V4F.js";
7
+ import "./chunk-LBGHZLLH.js";
8
8
  import "./chunk-PBGOZMVY.js";
9
9
  import "./chunk-VRGRAQDG.js";
10
10
  import "./chunk-NWO6GRYE.js";
@@ -42,4 +42,4 @@ export {
42
42
  runAgentTurn,
43
43
  runChatTurn
44
44
  };
45
- //# sourceMappingURL=orchestrator-ZFBVVQBO.js.map
45
+ //# sourceMappingURL=orchestrator-O4FZ3UVV.js.map
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openfox",
3
- "version": "2.0.38",
3
+ "version": "2.0.40",
4
4
  "description": "Local-LLM-first agentic coding assistant",
5
5
  "type": "module",
6
6
  "bin": {
@@ -3,8 +3,8 @@ import {
3
3
  finalizeTurnCompletion,
4
4
  generateSessionNameForSession
5
5
  } from "./chunk-JGNAYJJZ.js";
6
- import "./chunk-HJ7KRYWX.js";
7
- import "./chunk-DL6ZILAF.js";
6
+ import "./chunk-LH5J7V4F.js";
7
+ import "./chunk-LBGHZLLH.js";
8
8
  import "./chunk-PBGOZMVY.js";
9
9
  import "./chunk-VRGRAQDG.js";
10
10
  import "./chunk-NWO6GRYE.js";
@@ -171,7 +171,7 @@ var QueueProcessor = class {
171
171
  backend: provider?.backend ?? llmClient.getBackend(),
172
172
  model: llmClient.getModel()
173
173
  };
174
- const { runChatTurn } = await import("./orchestrator-ZFBVVQBO.js");
174
+ const { runChatTurn } = await import("./orchestrator-O4FZ3UVV.js");
175
175
  const runChatTurnParams = buildRunChatTurnParams({
176
176
  sessionManager,
177
177
  sessionId,
@@ -216,4 +216,4 @@ var QueueProcessor = class {
216
216
  export {
217
217
  QueueProcessor
218
218
  };
219
- //# sourceMappingURL=processor-NG4NH2SS.js.map
219
+ //# sourceMappingURL=processor-PQ5IQ24D.js.map
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  VERSION,
3
3
  createServer
4
- } from "./chunk-ATBNY4CL.js";
5
- import "./chunk-GZZW4XSI.js";
6
- import "./chunk-FFQCZ2MI.js";
7
- import "./chunk-HJ7KRYWX.js";
8
- import "./chunk-DL6ZILAF.js";
4
+ } from "./chunk-J7QZK6WE.js";
5
+ import "./chunk-EZJFNX7D.js";
6
+ import "./chunk-WUG3TOAL.js";
7
+ import "./chunk-LH5J7V4F.js";
8
+ import "./chunk-LBGHZLLH.js";
9
9
  import "./chunk-PBGOZMVY.js";
10
10
  import "./chunk-VRGRAQDG.js";
11
11
  import "./chunk-NWO6GRYE.js";
@@ -199,4 +199,4 @@ async function runServe(options) {
199
199
  export {
200
200
  runServe
201
201
  };
202
- //# sourceMappingURL=serve-VIWHYE6X.js.map
202
+ //# sourceMappingURL=serve-NC3U2VTG.js.map