openfox 2.0.74 → 2.0.76

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 (61) hide show
  1. package/README.md +5 -2
  2. package/dist/{chat-handler-ETKFIHUO.js → chat-handler-3BZ4PDL2.js} +17 -16
  3. package/dist/chunk-46RORSKU.js +495 -0
  4. package/dist/{chunk-S7O77CVO.js → chunk-4QHDQP2N.js} +2 -2
  5. package/dist/{chunk-YD6NDTKF.js → chunk-5GOOSZX7.js} +1 -1
  6. package/dist/{chunk-XWBNYU2L.js → chunk-7772ENZN.js} +4 -3
  7. package/dist/{chunk-I4RWK2L2.js → chunk-7QXRQ5ZL.js} +3 -3
  8. package/dist/{chunk-2VBETAU2.js → chunk-D6CI2B7B.js} +111 -539
  9. package/dist/{chunk-E5LLEO3C.js → chunk-DH3SSBZN.js} +2 -2
  10. package/dist/{chunk-CHPRGEY5.js → chunk-HAQV6AJP.js} +3 -3
  11. package/dist/{chunk-R6L2QXQ5.js → chunk-HHUIXHHX.js} +16 -6
  12. package/dist/{chunk-ZRQDCQ7R.js → chunk-HLHGNHMV.js} +10 -8
  13. package/dist/{chunk-QFR6JF32.js → chunk-J7VCI5EX.js} +205 -82
  14. package/dist/chunk-KQEVAO2Q.js +7 -0
  15. package/dist/{chunk-UN26GIQG.js → chunk-KVRSAC4Z.js} +1 -1
  16. package/dist/{chunk-UV3EMWL4.js → chunk-LXFLRM7M.js} +2 -2
  17. package/dist/{chunk-6XKM25OT.js → chunk-NGJ5XJ7D.js} +4 -4
  18. package/dist/{chunk-NOU2QIL5.js → chunk-O3NUNU4H.js} +5 -2
  19. package/dist/{chunk-PYB2UEC3.js → chunk-OBQPXUXI.js} +4 -4
  20. package/dist/{chunk-OHGQILRF.js → chunk-OCVRIPQP.js} +2 -2
  21. package/dist/{chunk-TMRFTWXP.js → chunk-OMWVB2XC.js} +38 -23
  22. package/dist/{chunk-6XOA2WPU.js → chunk-W4ZEG3LX.js} +5 -2
  23. package/dist/{chunk-2DCJERYX.js → chunk-XTK6E2QR.js} +4 -4
  24. package/dist/{chunk-QNKXZH3Z.js → chunk-YMFCFMMB.js} +2 -2
  25. package/dist/cli/dev.js +1 -1
  26. package/dist/cli/index.js +1 -1
  27. package/dist/{client-SC6X65GY.js → client-TDPQVBQ7.js} +2 -2
  28. package/dist/{compactor-4UKYWC5F.js → compactor-BB3YYVRP.js} +7 -7
  29. package/dist/{events-AEQ6GQXI.js → events-FLWRLM6V.js} +3 -3
  30. package/dist/{inspect-proxy-PHVZBZUO.js → inspect-proxy-JHZV34QR.js} +4 -4
  31. package/dist/install-CPOPMCSE.js +120 -0
  32. package/dist/{manager-24C2AXVF.js → manager-OPMCSYN2.js} +5 -5
  33. package/dist/{orchestrator-O2YAAZ3U.js → orchestrator-5IMCWX5P.js} +16 -15
  34. package/dist/package.json +1 -1
  35. package/dist/path-security-ZROWNL22.js +50 -0
  36. package/dist/{platform-XQ7BS3KQ.js → platform-QGXO3CNR.js} +4 -4
  37. package/dist/plugins-registry.json +6 -0
  38. package/dist/{processor-FKAS3K4L.js → processor-3R4GS6VB.js} +17 -16
  39. package/dist/{project-creator-FQRBN3RG.js → project-creator-C22J43BC.js} +3 -3
  40. package/dist/{projects-LHRNCUGW.js → projects-WSVINK2C.js} +3 -3
  41. package/dist/{protocol-4QFU7WAI.js → protocol-EWEVW65B.js} +3 -3
  42. package/dist/{protocol-DGQWVFcP.d.ts → protocol-Sr6Jr4ZS.d.ts} +5 -2
  43. package/dist/{provider-OJUCZ5RN.js → provider-G2YC2IC4.js} +3 -3
  44. package/dist/{provider-manager-MAKXSL6L.js → provider-manager-SQIELYLK.js} +3 -3
  45. package/dist/{serve-T5U6SLB7.js → serve-MXKMKZYY.js} +22 -21
  46. package/dist/server/index.d.ts +1 -1
  47. package/dist/server/index.js +21 -20
  48. package/dist/{server-T2YP3VT2.js → server-SPYFMFQE.js} +18 -17
  49. package/dist/{settings-PFXRYXZU.js → settings-7XDMSPIC.js} +3 -3
  50. package/dist/shared/index.d.ts +1 -1
  51. package/dist/shared/index.js +1 -1
  52. package/dist/{tools-APW7MY4E.js → tools-JRCUWPJO.js} +20 -18
  53. package/dist/{update-FWO365SV.js → update-OXDQ75M7.js} +12 -3
  54. package/dist/{update-server-MYYQUNZX.js → update-server-4MTM7M5P.js} +2 -2
  55. package/dist/web/assets/{index-DZRcUhoE.css → index-BNVBxKnB.css} +1 -1
  56. package/dist/web/assets/{index-Xh2VHbZe.js → index-ghDkIdlf.js} +74 -74
  57. package/dist/web/index.html +2 -2
  58. package/dist/web/sw.js +1 -1
  59. package/package.json +1 -1
  60. package/plugins-registry.json +6 -0
  61. package/dist/chunk-3OZBPWHP.js +0 -7
@@ -1,3 +1,9 @@
1
+ import {
2
+ createMcpTools
3
+ } from "./chunk-NWO6GRYE.js";
4
+ import {
5
+ applyMcpServerUpdate
6
+ } from "./chunk-KVRSAC4Z.js";
1
7
  import {
2
8
  getGitBranch,
3
9
  listWorkspaces
@@ -18,13 +24,13 @@ import {
18
24
  createToolCallEvent,
19
25
  createToolResultEvent,
20
26
  streamLLMPure
21
- } from "./chunk-XWBNYU2L.js";
27
+ } from "./chunk-7772ENZN.js";
22
28
  import {
23
29
  startInspectProxy
24
- } from "./chunk-E5LLEO3C.js";
30
+ } from "./chunk-DH3SSBZN.js";
25
31
  import {
26
32
  getProject
27
- } from "./chunk-UV3EMWL4.js";
33
+ } from "./chunk-LXFLRM7M.js";
28
34
  import {
29
35
  checkAborted,
30
36
  createProcess,
@@ -35,24 +41,30 @@ import {
35
41
  spawnShellProcess,
36
42
  startProcessCommand,
37
43
  stopProcess
38
- } from "./chunk-PYB2UEC3.js";
44
+ } from "./chunk-OBQPXUXI.js";
45
+ import {
46
+ SETTINGS_KEYS,
47
+ deleteSetting,
48
+ getSetting,
49
+ setSetting
50
+ } from "./chunk-YMFCFMMB.js";
39
51
  import {
40
52
  getMaxPerSession,
41
53
  getSessionProcessCount,
42
54
  terminateProcessTree
43
55
  } from "./chunk-I72ACQ6R.js";
44
56
  import {
45
- createMcpTools
46
- } from "./chunk-NWO6GRYE.js";
47
- import {
48
- applyMcpServerUpdate
49
- } from "./chunk-UN26GIQG.js";
57
+ PathAccessDeniedError,
58
+ extractAbsolutePathsFromCommand,
59
+ extractSensitivePathsFromCommand,
60
+ requestPathAccess
61
+ } from "./chunk-46RORSKU.js";
50
62
  import {
51
63
  getCurrentContextWindowId,
52
64
  getCurrentWindowMessageOptions,
53
65
  getEventStore,
54
66
  getRuntimeConfig
55
- } from "./chunk-6XKM25OT.js";
67
+ } from "./chunk-NGJ5XJ7D.js";
56
68
  import {
57
69
  buildContextMessagesFromEventHistory,
58
70
  foldContextState,
@@ -67,15 +79,8 @@ import {
67
79
  createChatDoneMessage,
68
80
  createChatMessageMessage,
69
81
  createChatMessageUpdatedMessage,
70
- createChatPathConfirmationMessage,
71
82
  createQueueStateMessage
72
- } from "./chunk-NOU2QIL5.js";
73
- import {
74
- SETTINGS_KEYS,
75
- deleteSetting,
76
- getSetting,
77
- setSetting
78
- } from "./chunk-QNKXZH3Z.js";
83
+ } from "./chunk-O3NUNU4H.js";
79
84
  import {
80
85
  modelSupportsVision
81
86
  } from "./chunk-V4IE7HJY.js";
@@ -99,430 +104,10 @@ import {
99
104
 
100
105
  // src/server/tools/read.ts
101
106
  import { readFile as readFile2, stat, readdir } from "fs/promises";
102
- import { extname, join as join2 } from "path";
103
-
104
- // src/server/tools/tool-helpers.ts
105
- import { resolve as resolve2, isAbsolute } from "path";
106
-
107
- // src/server/tools/path-security.ts
108
- import { realpath } from "fs/promises";
109
- import { resolve, normalize, join, basename, sep, posix, win32 } from "path";
110
- import { homedir, tmpdir } from "os";
111
- var SAFE_PATHS = /* @__PURE__ */ new Set([
112
- "/dev/null",
113
- "/dev/zero",
114
- "/dev/random",
115
- "/dev/urandom",
116
- "/dev/stdin",
117
- "/dev/stdout",
118
- "/dev/stderr",
119
- "/dev/fd/0",
120
- "/dev/fd/1",
121
- "/dev/fd/2"
122
- ]);
123
- var ALLOWED_ROOTS = ["/tmp", "/var/tmp", tmpdir()];
124
- var SENSITIVE_FILE_PATTERNS = [
125
- // Dotenv files (but not .envrc, .env-example, .env.example)
126
- /^\.env$/,
127
- // .env
128
- /^\.env\.(?!example)[a-zA-Z0-9_.-]+$/,
129
- // .env.local, .env.production (not .env.example)
130
- // Credential files
131
- /^credentials\.json$/i,
132
- /^secrets?\.(?:json|ya?ml|toml)$/i,
133
- // Private keys
134
- /\.pem$/i,
135
- /\.key$/i,
136
- /^id_rsa/,
137
- /^id_ed25519/,
138
- /^id_ecdsa/,
139
- /^id_dsa/,
140
- // Auth configs
141
- /^\.netrc$/
142
- ];
143
- function isSensitivePath(path) {
144
- const fileName = basename(path);
145
- if (!fileName) return false;
146
- return SENSITIVE_FILE_PATTERNS.some((pattern) => pattern.test(fileName));
147
- }
148
- var sessionAllowedPaths = /* @__PURE__ */ new Map();
149
- function addAllowedPath(sessionId, path) {
150
- if (!sessionAllowedPaths.has(sessionId)) {
151
- sessionAllowedPaths.set(sessionId, /* @__PURE__ */ new Set());
152
- }
153
- sessionAllowedPaths.get(sessionId).add(normalize(path));
154
- }
155
- function addAllowedPaths(sessionId, paths) {
156
- for (const path of paths) {
157
- addAllowedPath(sessionId, path);
158
- }
159
- }
160
- function isPathAllowed(sessionId, path) {
161
- const allowed = sessionAllowedPaths.get(sessionId);
162
- if (!allowed) return false;
163
- return allowed.has(normalize(path));
164
- }
165
- async function safeRealpath(path) {
166
- const absolutePath = normalize(resolve(path));
167
- try {
168
- return await realpath(absolutePath);
169
- } catch {
170
- const missingSegments = [];
171
- let current = absolutePath;
172
- while (true) {
173
- try {
174
- const canonicalParent = await realpath(current);
175
- return normalize(join(canonicalParent, ...missingSegments.reverse()));
176
- } catch {
177
- const parent = resolve(current, "..");
178
- if (parent === current) return absolutePath;
179
- missingSegments.push(basename(current));
180
- current = parent;
181
- }
182
- }
183
- }
184
- }
185
- async function isPathWithinSandbox(path, workdir, sessionId) {
186
- const normalizedWorkdir = normalize((await safeRealpath(workdir)).replace(/\/+$/, ""));
187
- let resolvedPath;
188
- try {
189
- resolvedPath = normalize(await realpath(path));
190
- } catch {
191
- try {
192
- const { readlink } = await import("fs/promises");
193
- const linkTarget = await readlink(path);
194
- const linkDir = resolve(path, "..");
195
- resolvedPath = await safeRealpath(resolve(linkDir, linkTarget));
196
- } catch {
197
- resolvedPath = await safeRealpath(path);
198
- }
199
- }
200
- resolvedPath = resolvedPath.replace(/\/+$/, "");
201
- if (resolvedPath === normalizedWorkdir || resolvedPath.startsWith(normalizedWorkdir + sep)) {
202
- return { allowed: true, resolvedPath };
203
- }
204
- for (const root of ALLOWED_ROOTS) {
205
- const normalizedRoot = normalize((await safeRealpath(root)).replace(/\/+$/, ""));
206
- if (resolvedPath === normalizedRoot || resolvedPath.startsWith(normalizedRoot + sep)) {
207
- return { allowed: true, resolvedPath };
208
- }
209
- }
210
- if (sessionId && (isPathAllowed(sessionId, path) || isPathAllowed(sessionId, resolvedPath))) {
211
- return { allowed: true, resolvedPath };
212
- }
213
- return { allowed: false, resolvedPath };
214
- }
215
- function looksLikeRegex(str) {
216
- return /[*?+[\]\\]/.test(str);
217
- }
218
- var isWindows = () => process.platform === "win32";
219
- function isWindowsAbsolutePath(str) {
220
- return /^[A-Za-z]:[\\/]/.test(str);
221
- }
222
- function normalizeExtracted(path) {
223
- return isWindowsAbsolutePath(path) ? win32.normalize(path) : posix.normalize(path);
224
- }
225
- function isPlaceholderToken(str) {
226
- return str.includes("__URL__") || str.includes("__FILEURL__") || str.includes("__SED__") || str.includes("__COMMIT_MSG__");
227
- }
228
- function extractAbsolutePathsFromCommand(command) {
229
- if (!command.trim()) {
230
- return [];
231
- }
232
- const paths = [];
233
- const home = homedir();
234
- let sanitized = command.replace(/https?:\/\/[^\s'"]+/g, " __URL__ ").replace(/ftp:\/\/[^\s'"]+/g, " __URL__ ");
235
- sanitized = sanitized.replace(/(?<!\w)s\/[^/]*\/[^/]*\/[gip]*/g, " __SED__ ");
236
- sanitized = sanitized.replace(/(?<!\w)s\|[^|]*\|[^|]*\|[gip]*/g, " __SED__ ");
237
- sanitized = sanitized.replace(/(?<!\w)s:[^:]*:[^:]*:[gip]*/g, " __SED__ ");
238
- sanitized = sanitized.replace(
239
- /git\s+commit\b.*?(?:-(?:[a-zA-Z]*m)(?=\s)|--message)\s+(["'])(?:(?!\1).)*\1/g,
240
- (match2) => match2.replace(/\/[^\s"'|&;<>`()]+/g, " __COMMIT_MSG__ ")
241
- );
242
- const fileUrlMatches = command.matchAll(/file:\/\/([^\s'"]+)/g);
243
- for (const match2 of fileUrlMatches) {
244
- const filePath = match2[1];
245
- if (filePath && !isSafePath(filePath)) {
246
- paths.push(normalizeExtracted(filePath));
247
- }
248
- }
249
- sanitized = sanitized.replace(/file:\/\/[^\s'"]+/g, " __FILEURL__ ");
250
- const tildePattern = /(?:^|[\s='"(])~(\/[^\s'"()]*)?(?=[\s'"()]|$)/g;
251
- let match;
252
- while ((match = tildePattern.exec(sanitized)) !== null) {
253
- const pathPart = match[1] ?? "";
254
- const fullPath = home + pathPart;
255
- const resolved = normalize(resolve(fullPath));
256
- if (!isSafePath(resolved)) {
257
- paths.push(resolved);
258
- }
259
- }
260
- const quotedPattern = /["']([^"']+)["']/g;
261
- while ((match = quotedPattern.exec(sanitized)) !== null) {
262
- const content = match[1];
263
- if (content.startsWith("/") && content.endsWith("/")) {
264
- continue;
265
- }
266
- if (content.startsWith("/") && looksLikeRegex(content)) {
267
- continue;
268
- }
269
- if (isWindowsAbsolutePath(content)) {
270
- const resolved = normalizeExtracted(content);
271
- if (!isSafePath(resolved)) {
272
- paths.push(resolved);
273
- }
274
- } else if (!isWindows() && content.startsWith("/")) {
275
- const resolved = normalizeExtracted(content);
276
- if (!isSafePath(resolved)) {
277
- paths.push(resolved);
278
- }
279
- } else if (content.startsWith("~")) {
280
- const pathPart = content.slice(1);
281
- const fullPath = join(home, pathPart);
282
- const resolved = normalize(resolve(fullPath));
283
- if (!isSafePath(resolved)) {
284
- paths.push(resolved);
285
- }
286
- }
287
- }
288
- if (isWindows()) {
289
- const winAbsolutePattern = /(?:^|[\s=(])([A-Za-z]:[\\/][^\s"'|&;<>`()]+)/g;
290
- while ((match = winAbsolutePattern.exec(sanitized)) !== null) {
291
- const candidate = match[1];
292
- if (isPlaceholderToken(candidate)) continue;
293
- const resolved = normalizeExtracted(candidate);
294
- if (!isSafePath(resolved)) {
295
- paths.push(resolved);
296
- }
297
- }
298
- } else {
299
- const absolutePattern = /(?:^|[\s=(])(\/[^\s"'|&;<>`()]+)/g;
300
- while ((match = absolutePattern.exec(sanitized)) !== null) {
301
- const candidate = match[1];
302
- if (isPlaceholderToken(candidate)) continue;
303
- if (looksLikeRegex(candidate)) continue;
304
- const resolved = normalizeExtracted(candidate);
305
- if (resolved === "/" || resolved === "") continue;
306
- if (!isSafePath(resolved)) {
307
- paths.push(resolved);
308
- }
309
- }
310
- }
311
- return [...new Set(paths)];
312
- }
313
- function extractSensitivePathsFromCommand(command) {
314
- if (!command.trim()) {
315
- return [];
316
- }
317
- const paths = [];
318
- const quotedPattern = /["']([^"']+)["']/g;
319
- let match;
320
- while ((match = quotedPattern.exec(command)) !== null) {
321
- const content = match[1];
322
- if (isSensitivePath(content)) {
323
- paths.push(content);
324
- }
325
- }
326
- const unquoted = command.replace(/["'][^"']*["']/g, " ").replace(/[|&;><]/g, " ");
327
- const tokens = unquoted.split(/\s+/).filter(Boolean);
328
- for (const token of tokens) {
329
- if (token.startsWith("-")) continue;
330
- if (token.includes("://")) continue;
331
- if (isSensitivePath(token)) {
332
- paths.push(token);
333
- }
334
- }
335
- return [...new Set(paths)];
336
- }
337
- function isSafePath(path) {
338
- const normalized = posix.normalize(path);
339
- return SAFE_PATHS.has(normalized);
340
- }
341
- async function checkPathsAccess(paths, workdir, sessionId) {
342
- if (paths.length === 0) {
343
- return { needsConfirmation: false, deniedPaths: [], sensitivePaths: [] };
344
- }
345
- const deniedPaths = [];
346
- const sensitivePaths = [];
347
- for (const path of paths) {
348
- const result = await isPathWithinSandbox(path, workdir, sessionId);
349
- if (!result.allowed) {
350
- deniedPaths.push(result.resolvedPath);
351
- }
352
- if (isSensitivePath(path) && !isPathAllowed(sessionId ?? "", path)) {
353
- sensitivePaths.push(result.resolvedPath);
354
- }
355
- }
356
- const uniqueDenied = [...new Set(deniedPaths)];
357
- const uniqueSensitive = [...new Set(sensitivePaths)];
358
- return {
359
- needsConfirmation: uniqueDenied.length > 0 || uniqueSensitive.length > 0,
360
- deniedPaths: uniqueDenied,
361
- sensitivePaths: uniqueSensitive
362
- };
363
- }
364
- var DANGEROUS_PATTERNS = [
365
- /sudo\s/,
366
- /rm\s+(-rf?|--recursive)\s+[~]/,
367
- /chmod\s+777/,
368
- />\s*\/dev\/sd/,
369
- /mkfs\s/,
370
- /dd\s+if=/,
371
- /:\(\)\s*\{\s*:\s*\|\s*:\s*&\s*\}\s*;/
372
- ];
373
- function extractDangerousPatterns(command) {
374
- const dangerous = [];
375
- for (const pattern of DANGEROUS_PATTERNS) {
376
- if (pattern.test(command)) {
377
- dangerous.push(pattern.source);
378
- }
379
- }
380
- return dangerous;
381
- }
382
- function extractGitNoVerify(command) {
383
- const subCommands = command.split(/\s*(?:&&|\|\||\||;)\s*/);
384
- for (const sub of subCommands) {
385
- const parts = sub.trim().split(/\s+/);
386
- const gitIndex = parts.indexOf("git");
387
- const subCmd = parts[gitIndex + 1];
388
- if (gitIndex >= 0 && subCmd && !subCmd.startsWith("-")) {
389
- const gitArgs = parts.slice(gitIndex + 2);
390
- if (gitArgs.some((a) => a === "--no-verify" || a === "-n")) {
391
- return true;
392
- }
393
- }
394
- }
395
- return false;
396
- }
397
- async function requestPathAccess(paths, workdir, sessionId, callId, tool, onEvent, dangerLevel, command, isSubAgent) {
398
- if (isSubAgent) {
399
- const result2 = await checkPathsAccess(paths, workdir, sessionId);
400
- if (!result2.needsConfirmation) return;
401
- if (dangerLevel === "dangerous") {
402
- const allPaths2 = [.../* @__PURE__ */ new Set([...result2.deniedPaths, ...result2.sensitivePaths])];
403
- addAllowedPaths(sessionId, allPaths2);
404
- return;
405
- }
406
- const allPaths = [.../* @__PURE__ */ new Set([...result2.deniedPaths, ...result2.sensitivePaths])];
407
- const hasDenied2 = result2.deniedPaths.length > 0;
408
- const hasSensitive2 = result2.sensitivePaths.length > 0;
409
- const reason2 = hasDenied2 && hasSensitive2 ? "both" : hasDenied2 ? "outside_workdir" : "sensitive_file";
410
- throw new PathAccessDeniedError(allPaths, tool, reason2);
411
- }
412
- const emitPendingEvent = (confirmationPaths, confirmationReason) => {
413
- try {
414
- const eventStore = getEventStore();
415
- eventStore.append(sessionId, {
416
- type: "path.confirmation_pending",
417
- data: { callId, tool, paths: confirmationPaths, workdir, reason: confirmationReason }
418
- });
419
- } catch {
420
- }
421
- };
422
- if (command && extractGitNoVerify(command)) {
423
- emitPendingEvent([workdir], "git_no_verify");
424
- const confirmationPromise2 = registerPathConfirmation(callId, [workdir], sessionId);
425
- onEvent(createChatPathConfirmationMessage(callId, tool, ["git --no-verify detected"], workdir, "git_no_verify"));
426
- const approved2 = await confirmationPromise2;
427
- if (!approved2) {
428
- throw new PathAccessDeniedError(
429
- ["git --no-verify"],
430
- tool,
431
- "git_no_verify",
432
- "User denied git command with --no-verify. The agent must not use --no-verify and must resolve the issue (e.g., fix lint errors, resolve conflicts) that prevents the commit."
433
- );
434
- }
435
- }
436
- if (dangerLevel !== "dangerous" && command) {
437
- const dangerousPatterns = extractDangerousPatterns(command);
438
- if (dangerousPatterns.length > 0) {
439
- emitPendingEvent([workdir], "dangerous_command");
440
- const confirmationPromise2 = registerPathConfirmation(callId, [workdir], sessionId);
441
- onEvent(
442
- createChatPathConfirmationMessage(callId, tool, [dangerousPatterns.join(", ")], workdir, "dangerous_command")
443
- );
444
- const approved2 = await confirmationPromise2;
445
- if (!approved2) {
446
- throw new PathAccessDeniedError(dangerousPatterns, tool, "dangerous_command");
447
- }
448
- }
449
- }
450
- const result = await checkPathsAccess(paths, workdir, sessionId);
451
- if (!result.needsConfirmation) {
452
- return;
453
- }
454
- if (dangerLevel === "dangerous") {
455
- const allPaths = [.../* @__PURE__ */ new Set([...result.deniedPaths, ...result.sensitivePaths])];
456
- addAllowedPaths(sessionId, allPaths);
457
- return;
458
- }
459
- const allPathsNeedingConfirmation = [.../* @__PURE__ */ new Set([...result.deniedPaths, ...result.sensitivePaths])];
460
- const hasDenied = result.deniedPaths.length > 0;
461
- const hasSensitive = result.sensitivePaths.length > 0;
462
- const reason = hasDenied && hasSensitive ? "both" : hasDenied ? "outside_workdir" : "sensitive_file";
463
- emitPendingEvent(allPathsNeedingConfirmation, reason);
464
- const confirmationPromise = registerPathConfirmation(callId, allPathsNeedingConfirmation, sessionId);
465
- onEvent(createChatPathConfirmationMessage(callId, tool, allPathsNeedingConfirmation, workdir, reason));
466
- const approved = await confirmationPromise;
467
- if (!approved) {
468
- throw new PathAccessDeniedError(allPathsNeedingConfirmation, tool, reason);
469
- }
470
- }
471
- var PathAccessDeniedError = class extends Error {
472
- constructor(paths, tool, reason = "outside_workdir", customMessage) {
473
- const reasonText = reason === "sensitive_file" ? "sensitive files (may contain secrets)" : reason === "both" ? "paths outside workdir and sensitive files" : reason === "git_no_verify" ? "git commands with --no-verify" : "paths outside workdir";
474
- super(customMessage ?? `User denied access to ${reasonText}: ${paths.join(", ")}`);
475
- this.paths = paths;
476
- this.tool = tool;
477
- this.reason = reason;
478
- this.customMessage = customMessage;
479
- this.name = "PathAccessDeniedError";
480
- }
481
- };
482
- var pendingConfirmations = /* @__PURE__ */ new Map();
483
- function registerPathConfirmation(callId, paths, sessionId) {
484
- return new Promise((resolve6, reject) => {
485
- pendingConfirmations.set(callId, { resolve: resolve6, reject, paths, sessionId });
486
- });
487
- }
488
- function providePathConfirmation(callId, approved, alwaysAllow) {
489
- const pending = pendingConfirmations.get(callId);
490
- if (!pending) {
491
- return { found: false };
492
- }
493
- try {
494
- const eventStore = getEventStore();
495
- eventStore.append(pending.sessionId, {
496
- type: "path.confirmation_responded",
497
- data: { callId, approved, alwaysAllow: alwaysAllow ?? false }
498
- });
499
- } catch {
500
- }
501
- if (approved) {
502
- if (alwaysAllow) {
503
- addAllowedPaths(pending.sessionId, pending.paths);
504
- } else {
505
- addAllowedPaths(pending.sessionId, pending.paths);
506
- }
507
- }
508
- pending.resolve(approved);
509
- pendingConfirmations.delete(callId);
510
- return { found: true, sessionId: pending.sessionId, approved };
511
- }
512
- function cancelPathConfirmationsForSession(sessionId, reason) {
513
- let cancelledCount = 0;
514
- for (const [callId, pending] of pendingConfirmations.entries()) {
515
- if (pending.sessionId !== sessionId) {
516
- continue;
517
- }
518
- pending.reject(new Error(reason));
519
- pendingConfirmations.delete(callId);
520
- cancelledCount += 1;
521
- }
522
- return cancelledCount;
523
- }
107
+ import { extname, join } from "path";
524
108
 
525
109
  // src/server/tools/tool-helpers.ts
110
+ import { resolve, isAbsolute } from "path";
526
111
  function buildSignal(timeoutMs, contextSignal) {
527
112
  const timeoutSignal = AbortSignal.timeout(timeoutMs);
528
113
  if (contextSignal) {
@@ -570,7 +155,7 @@ function createTool(name, definition, handler2) {
570
155
  async execute(args, context) {
571
156
  const startTime = Date.now();
572
157
  const helpers = {
573
- resolvePath: (path) => isAbsolute(path) ? path : resolve2(context.workdir, path),
158
+ resolvePath: (path) => isAbsolute(path) ? path : resolve(context.workdir, path),
574
159
  checkPathAccess: async (paths, command) => {
575
160
  if (context.onEvent) {
576
161
  await requestPathAccess(
@@ -620,7 +205,7 @@ function createTool(name, definition, handler2) {
620
205
  // src/server/tools/file-tracker.ts
621
206
  import { createHash } from "crypto";
622
207
  import { readFile } from "fs/promises";
623
- import { resolve as resolve3 } from "path";
208
+ import { resolve as resolve2 } from "path";
624
209
  var FileNotReadError = class extends Error {
625
210
  constructor(path) {
626
211
  super(`File "${path}" must be read before writing. Use read_file first.`);
@@ -649,7 +234,7 @@ async function computeFileHash(filePath) {
649
234
  }
650
235
  }
651
236
  async function validateFileForWrite(filePath, readFiles) {
652
- const normalizedPath = resolve3(filePath);
237
+ const normalizedPath = resolve2(filePath);
653
238
  const currentHash = await computeFileHash(normalizedPath);
654
239
  if (currentHash === null) {
655
240
  return { valid: true };
@@ -889,7 +474,7 @@ async function listDirectory(dirPath, relativePath) {
889
474
  let sizeStr = "";
890
475
  if (!entry.isDirectory()) {
891
476
  try {
892
- const entryStat = await stat(join2(dirPath, entry.name));
477
+ const entryStat = await stat(join(dirPath, entry.name));
893
478
  sizeStr = ` ${formatSize(entryStat.size).padStart(7)}`;
894
479
  } catch {
895
480
  }
@@ -1402,7 +987,7 @@ Make sure whitespace and indentation match exactly.`
1402
987
 
1403
988
  // src/server/tools/shell.ts
1404
989
  import { spawn } from "child_process";
1405
- import { resolve as resolve4, isAbsolute as isAbsolute2 } from "path";
990
+ import { resolve as resolve3, isAbsolute as isAbsolute2 } from "path";
1406
991
  import { access } from "fs/promises";
1407
992
  import stripAnsi from "strip-ansi";
1408
993
 
@@ -1428,7 +1013,7 @@ async function checkRtkAvailability() {
1428
1013
  rtkAvailable = true;
1429
1014
  } catch {
1430
1015
  try {
1431
- const out = await new Promise((resolve6, reject) => {
1016
+ const out = await new Promise((resolve5, reject) => {
1432
1017
  const proc = spawn("rtk", ["--version"], { stdio: ["ignore", "pipe", "pipe"] });
1433
1018
  let output = "";
1434
1019
  proc.stdout?.on("data", (d) => {
@@ -1436,7 +1021,7 @@ async function checkRtkAvailability() {
1436
1021
  });
1437
1022
  proc.on("error", reject);
1438
1023
  proc.on("close", (code) => {
1439
- if (code === 0) resolve6(output.trim());
1024
+ if (code === 0) resolve5(output.trim());
1440
1025
  else reject(new Error(`exit ${code}`));
1441
1026
  });
1442
1027
  });
@@ -1495,12 +1080,12 @@ var runCommandTool = createTool(
1495
1080
  const pathsToCheck = [workingDir];
1496
1081
  const commandPaths = extractAbsolutePathsFromCommand(args.command);
1497
1082
  for (const cmdPath of commandPaths) {
1498
- const resolved = isAbsolute2(cmdPath) ? cmdPath : resolve4(workingDir, cmdPath);
1083
+ const resolved = isAbsolute2(cmdPath) ? cmdPath : resolve3(workingDir, cmdPath);
1499
1084
  pathsToCheck.push(resolved);
1500
1085
  }
1501
1086
  const sensitivePaths = extractSensitivePathsFromCommand(args.command);
1502
1087
  for (const sensitivePath of sensitivePaths) {
1503
- const resolved = isAbsolute2(sensitivePath) ? sensitivePath : resolve4(workingDir, sensitivePath);
1088
+ const resolved = isAbsolute2(sensitivePath) ? sensitivePath : resolve3(workingDir, sensitivePath);
1504
1089
  pathsToCheck.push(resolved);
1505
1090
  }
1506
1091
  await helpers.checkPathAccess(pathsToCheck, args.command);
@@ -1550,7 +1135,7 @@ ${result.stderr}`;
1550
1135
  async function tryRtkRewrite(command) {
1551
1136
  if (!await checkRtkAvailability()) return command;
1552
1137
  try {
1553
- const result = await new Promise((resolve6, reject) => {
1138
+ const result = await new Promise((resolve5, reject) => {
1554
1139
  const proc = spawn("rtk", ["rewrite", command], {
1555
1140
  stdio: ["ignore", "pipe", "pipe"],
1556
1141
  timeout: 2e3
@@ -1561,7 +1146,7 @@ async function tryRtkRewrite(command) {
1561
1146
  });
1562
1147
  proc.on("error", reject);
1563
1148
  proc.on("close", (code) => {
1564
- if (code === 0 || code === 3) resolve6(stdout.trim());
1149
+ if (code === 0 || code === 3) resolve5(stdout.trim());
1565
1150
  else reject(new Error(`exit ${code}`));
1566
1151
  });
1567
1152
  });
@@ -1571,7 +1156,7 @@ async function tryRtkRewrite(command) {
1571
1156
  return command;
1572
1157
  }
1573
1158
  function executeCommand(command, cwd, timeout, signal, onProgress) {
1574
- return new Promise((resolve6, reject) => {
1159
+ return new Promise((resolve5, reject) => {
1575
1160
  if (checkAborted(signal)) {
1576
1161
  reject(new Error("Command aborted before execution"));
1577
1162
  return;
@@ -1610,7 +1195,7 @@ function executeCommand(command, cwd, timeout, signal, onProgress) {
1610
1195
  let output = stdout.trim();
1611
1196
  if (output) output += "\n\n";
1612
1197
  output += "[interrupted by user]";
1613
- resolve6({
1198
+ resolve5({
1614
1199
  stdout: output,
1615
1200
  stderr: stderr.trim(),
1616
1201
  exitCode: 130
@@ -1622,7 +1207,7 @@ function executeCommand(command, cwd, timeout, signal, onProgress) {
1622
1207
  if (output) output += "\n\n";
1623
1208
  output += `[Exit code: 124]
1624
1209
  [Process timed out after ${timeout}ms]`;
1625
- resolve6({
1210
+ resolve5({
1626
1211
  stdout: output,
1627
1212
  stderr: stderr.trim(),
1628
1213
  exitCode: 124
@@ -1638,7 +1223,7 @@ function executeCommand(command, cwd, timeout, signal, onProgress) {
1638
1223
  if (output) output += "\n\n";
1639
1224
  output += `[Exit code: 124]
1640
1225
  [Process timed out after ${timeout}ms]`;
1641
- resolve6({
1226
+ resolve5({
1642
1227
  stdout: output,
1643
1228
  stderr: stderr.trim(),
1644
1229
  exitCode: 124
@@ -1649,14 +1234,14 @@ function executeCommand(command, cwd, timeout, signal, onProgress) {
1649
1234
  let output = stdout.trim();
1650
1235
  if (output) output += "\n\n";
1651
1236
  output += "[interrupted by user]";
1652
- resolve6({
1237
+ resolve5({
1653
1238
  stdout: output,
1654
1239
  stderr: stderr.trim(),
1655
1240
  exitCode: 130
1656
1241
  });
1657
1242
  return;
1658
1243
  }
1659
- resolve6({
1244
+ resolve5({
1660
1245
  stdout: stdout.trim(),
1661
1246
  stderr: stderr.trim(),
1662
1247
  exitCode: code ?? 1
@@ -1821,18 +1406,18 @@ ${Object.entries(schema.fields).map(([f, t]) => ` ${f}: ${t}`).join("\n")}`
1821
1406
 
1822
1407
  // src/server/sub-agents/manager.ts
1823
1408
  import { readFile as readFile8, access as access5 } from "fs/promises";
1824
- import { join as join7, dirname as dirname5, isAbsolute as isAbsolute3 } from "path";
1409
+ import { join as join6, dirname as dirname5, isAbsolute as isAbsolute3 } from "path";
1825
1410
 
1826
1411
  // src/server/agents/registry.ts
1827
1412
  import { readdir as readdir3, readFile as readFile5, writeFile as writeFile4, mkdir as mkdir3, access as access3, unlink as unlink2 } from "fs/promises";
1828
- import { join as join4, dirname as dirname2 } from "path";
1413
+ import { join as join3, dirname as dirname2 } from "path";
1829
1414
  import { constants as constants2 } from "fs";
1830
1415
  import { fileURLToPath } from "url";
1831
1416
  import matter2 from "gray-matter";
1832
1417
 
1833
1418
  // src/server/shared/item-loader.ts
1834
1419
  import { readdir as readdir2, readFile as readFile4, access as access2, writeFile as writeFile3, mkdir as mkdir2, unlink } from "fs/promises";
1835
- import { join as join3 } from "path";
1420
+ import { join as join2 } from "path";
1836
1421
  import { constants } from "fs";
1837
1422
  import matter from "gray-matter";
1838
1423
  async function pathExists(path) {
@@ -1864,7 +1449,7 @@ async function loadItemsFromDir(dir, options) {
1864
1449
  const items = [];
1865
1450
  for (const file of files) {
1866
1451
  try {
1867
- const raw = await readFile4(join3(dir, file), "utf-8");
1452
+ const raw = await readFile4(join2(dir, file), "utf-8");
1868
1453
  const { data, content } = matter(raw);
1869
1454
  if (data.id && content.trim()) {
1870
1455
  items.push({
@@ -1888,12 +1473,12 @@ async function saveItemToDir(dir, item, ext, serialize) {
1888
1473
  await mkdir2(dir, { recursive: true });
1889
1474
  }
1890
1475
  const meta = item.metadata;
1891
- const filePath = join3(dir, `${meta.id}${ext}`);
1476
+ const filePath = join2(dir, `${meta.id}${ext}`);
1892
1477
  await writeFile3(filePath, serialize(item), "utf-8");
1893
1478
  }
1894
1479
  var jsonSerializer = (item) => JSON.stringify(item, null, 2) + "\n";
1895
1480
  async function deleteItemFromDir(dir, id, ext) {
1896
- const filePath = join3(dir, `${id}${ext}`);
1481
+ const filePath = join2(dir, `${id}${ext}`);
1897
1482
  try {
1898
1483
  await unlink(filePath);
1899
1484
  return { success: true };
@@ -1904,14 +1489,14 @@ async function deleteItemFromDir(dir, id, ext) {
1904
1489
 
1905
1490
  // src/server/agents/registry.ts
1906
1491
  var __bundleDir = dirname2(fileURLToPath(import.meta.url));
1907
- var DEFAULTS_DIR = join4(__bundleDir, "defaults");
1908
- var DEFAULTS_DIR_ALT = join4(__bundleDir, "agent-defaults");
1492
+ var DEFAULTS_DIR = join3(__bundleDir, "defaults");
1493
+ var DEFAULTS_DIR_ALT = join3(__bundleDir, "agent-defaults");
1909
1494
  var AGENT_EXTENSION = ".agent.md";
1910
1495
  function getAgentsDir(configDir) {
1911
- return join4(configDir, "agents");
1496
+ return join3(configDir, "agents");
1912
1497
  }
1913
1498
  function getProjectAgentsDir(projectDir) {
1914
- return join4(projectDir, ".openfox", "agents");
1499
+ return join3(projectDir, ".openfox", "agents");
1915
1500
  }
1916
1501
  async function pathExists2(path) {
1917
1502
  try {
@@ -1952,7 +1537,7 @@ async function loadAgentsFromDir(dir) {
1952
1537
  const agents = [];
1953
1538
  for (const file of files) {
1954
1539
  try {
1955
- const raw = await readFile5(join4(dir, file), "utf-8");
1540
+ const raw = await readFile5(join3(dir, file), "utf-8");
1956
1541
  const agent = parseAgentFile(raw, file);
1957
1542
  if (agent) {
1958
1543
  agents.push(agent);
@@ -2037,7 +1622,7 @@ async function isDefaultAgent(agentId) {
2037
1622
  }
2038
1623
  function getAgentFilePaths(agentsDir, agentId) {
2039
1624
  const hyphenated = agentId.replace(/_/g, "-");
2040
- return [join4(agentsDir, `${agentId}${AGENT_EXTENSION}`), join4(agentsDir, `${hyphenated}${AGENT_EXTENSION}`)];
1625
+ return [join3(agentsDir, `${agentId}${AGENT_EXTENSION}`), join3(agentsDir, `${hyphenated}${AGENT_EXTENSION}`)];
2041
1626
  }
2042
1627
  function findAgentById(agentId, agents) {
2043
1628
  return agents.find((a) => a.metadata.id === agentId);
@@ -2071,7 +1656,7 @@ async function saveAgent(configDir, agent) {
2071
1656
  if (!await pathExists2(agentsDir)) {
2072
1657
  await mkdir3(agentsDir, { recursive: true });
2073
1658
  }
2074
- const filePath = join4(agentsDir, `${agent.metadata.id}${AGENT_EXTENSION}`);
1659
+ const filePath = join3(agentsDir, `${agent.metadata.id}${AGENT_EXTENSION}`);
2075
1660
  const content = matter2.stringify(agent.prompt, agent.metadata);
2076
1661
  await writeFile4(filePath, content, "utf-8");
2077
1662
  }
@@ -2095,7 +1680,7 @@ async function deleteAgent(configDir, agentId) {
2095
1680
 
2096
1681
  // src/server/context/instructions.ts
2097
1682
  import { readFile as readFile6, access as access4 } from "fs/promises";
2098
- import { join as join5, dirname as dirname3 } from "path";
1683
+ import { join as join4, dirname as dirname3 } from "path";
2099
1684
  import { constants as constants3 } from "fs";
2100
1685
  var INSTRUCTION_FILENAMES = ["AGENTS.md", "CLAUDE.md"];
2101
1686
  async function findInstructionFiles(workdir) {
@@ -2112,7 +1697,7 @@ async function findInstructionFiles(workdir) {
2112
1697
  }
2113
1698
  for (const dir of pathsToCheck) {
2114
1699
  for (const filename of INSTRUCTION_FILENAMES) {
2115
- const filePath = join5(dir, filename);
1700
+ const filePath = join4(dir, filename);
2116
1701
  if (await fileExists(filePath)) {
2117
1702
  foundFiles.push({
2118
1703
  path: filePath,
@@ -2185,22 +1770,22 @@ async function fileExists(path) {
2185
1770
  }
2186
1771
 
2187
1772
  // src/server/skills/registry.ts
2188
- import { writeFile as writeFile5, mkdir as mkdir4, unlink as unlink3, readdir as readdir4, readFile as readFile7, realpath as realpath2, rm } from "fs/promises";
2189
- import { join as join6, dirname as dirname4 } from "path";
1773
+ import { writeFile as writeFile5, mkdir as mkdir4, unlink as unlink3, readdir as readdir4, readFile as readFile7, realpath, rm } from "fs/promises";
1774
+ import { join as join5, dirname as dirname4 } from "path";
2190
1775
  import { fileURLToPath as fileURLToPath2 } from "url";
2191
- import { homedir as homedir2 } from "os";
1776
+ import { homedir } from "os";
2192
1777
  import matter3 from "gray-matter";
2193
1778
  var __bundleDir2 = dirname4(fileURLToPath2(import.meta.url));
2194
- var DEFAULTS_DIR2 = join6(__bundleDir2, "defaults");
2195
- var DEFAULTS_DIR_ALT2 = join6(__bundleDir2, "skill-defaults");
1779
+ var DEFAULTS_DIR2 = join5(__bundleDir2, "defaults");
1780
+ var DEFAULTS_DIR_ALT2 = join5(__bundleDir2, "skill-defaults");
2196
1781
  var SKILL_EXTENSION = ".skill.md";
2197
1782
  var SKILL_SETTING_PREFIX = "skill.enabled.";
2198
1783
  var PORTABLE_NAME_REGEX = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
2199
1784
  function getSkillsDir(configDir) {
2200
- return join6(configDir, "skills");
1785
+ return join5(configDir, "skills");
2201
1786
  }
2202
1787
  function getProjectSkillsDir(projectDir) {
2203
- return join6(projectDir, ".openfox", "skills");
1788
+ return join5(projectDir, ".openfox", "skills");
2204
1789
  }
2205
1790
  function portableDisplayName(data, id) {
2206
1791
  const metadata = data["metadata"];
@@ -2228,8 +1813,8 @@ async function loadPortableSkills(dir, source) {
2228
1813
  const skills = [];
2229
1814
  for (const entry of entries) {
2230
1815
  if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
2231
- const packageDir = join6(dir, entry.name);
2232
- const entrypoint = join6(packageDir, "SKILL.md");
1816
+ const packageDir = join5(dir, entry.name);
1817
+ const entrypoint = join5(packageDir, "SKILL.md");
2233
1818
  try {
2234
1819
  const content = await readFile7(entrypoint, "utf-8");
2235
1820
  const parsed = matter3(content);
@@ -2238,7 +1823,7 @@ async function loadPortableSkills(dir, source) {
2238
1823
  const description = typeof data["description"] === "string" ? data["description"].trim() : "";
2239
1824
  const prompt = parsed.content.trim();
2240
1825
  if (!id || !description || !prompt) continue;
2241
- const resolvedDirectory = await realpath2(packageDir);
1826
+ const resolvedDirectory = await realpath(packageDir);
2242
1827
  const warnings = [];
2243
1828
  if (id.length > 64 || !PORTABLE_NAME_REGEX.test(id)) {
2244
1829
  warnings.push("Skill name must use 1-64 lowercase letters, numbers, and single hyphens");
@@ -2269,10 +1854,10 @@ async function loadPortableSkills(dir, source) {
2269
1854
  async function annotateLegacySkills(skills, dir, source) {
2270
1855
  return Promise.all(
2271
1856
  skills.map(async (skill) => {
2272
- const entrypoint = join6(dir, `${skill.metadata.id}${SKILL_EXTENSION}`);
1857
+ const entrypoint = join5(dir, `${skill.metadata.id}${SKILL_EXTENSION}`);
2273
1858
  let resolvedEntrypoint = entrypoint;
2274
1859
  try {
2275
- resolvedEntrypoint = await realpath2(entrypoint);
1860
+ resolvedEntrypoint = await realpath(entrypoint);
2276
1861
  } catch {
2277
1862
  }
2278
1863
  return {
@@ -2322,16 +1907,16 @@ async function loadAllSkills(configDir, projectDir, options = {}) {
2322
1907
  return (await loadAllSkillsWithDiagnostics(configDir, projectDir, options)).skills;
2323
1908
  }
2324
1909
  async function loadAllSkillsWithDiagnostics(configDir, projectDir, options = {}) {
2325
- const home = options.homeDir ?? homedir2();
1910
+ const home = options.homeDir ?? homedir();
2326
1911
  const selected = (options.selectedDirectories ?? getSelectedSkillDirectories()).map(
2327
- (dir) => dir === "~" ? home : dir.startsWith("~/") ? join6(home, dir.slice(2)) : dir
1912
+ (dir) => dir === "~" ? home : dir.startsWith("~/") ? join5(home, dir.slice(2)) : dir
2328
1913
  );
2329
1914
  const locations = [
2330
1915
  loadDefaultSkills(),
2331
- loadSkillsDirectory(join6(home, ".agents", "skills"), "global-shared"),
1916
+ loadSkillsDirectory(join5(home, ".agents", "skills"), "global-shared"),
2332
1917
  loadUserSkills(configDir),
2333
1918
  ...selected.map((dir) => loadSkillsDirectory(dir, "selected")),
2334
- ...projectDir ? [loadSkillsDirectory(join6(projectDir, ".agents", "skills"), "project-shared"), loadProjectSkills(projectDir)] : []
1919
+ ...projectDir ? [loadSkillsDirectory(join5(projectDir, ".agents", "skills"), "project-shared"), loadProjectSkills(projectDir)] : []
2335
1920
  ];
2336
1921
  const groups = await Promise.all(locations);
2337
1922
  const skillMap = /* @__PURE__ */ new Map();
@@ -2381,11 +1966,11 @@ function findSkillById(skillId, skills) {
2381
1966
  return skills.find((s) => s.metadata.id === skillId);
2382
1967
  }
2383
1968
  async function skillExists(configDir, skillId, projectDir) {
2384
- if (await pathExists(join6(getSkillsDir(configDir), `${skillId}${SKILL_EXTENSION}`))) return true;
2385
- if (await pathExists(join6(getSkillsDir(configDir), skillId, "SKILL.md"))) return true;
2386
- if (projectDir && await pathExists(join6(getProjectSkillsDir(projectDir), `${skillId}${SKILL_EXTENSION}`)))
1969
+ if (await pathExists(join5(getSkillsDir(configDir), `${skillId}${SKILL_EXTENSION}`))) return true;
1970
+ if (await pathExists(join5(getSkillsDir(configDir), skillId, "SKILL.md"))) return true;
1971
+ if (projectDir && await pathExists(join5(getProjectSkillsDir(projectDir), `${skillId}${SKILL_EXTENSION}`)))
2387
1972
  return true;
2388
- if (projectDir && await pathExists(join6(getProjectSkillsDir(projectDir), skillId, "SKILL.md"))) return true;
1973
+ if (projectDir && await pathExists(join5(getProjectSkillsDir(projectDir), skillId, "SKILL.md"))) return true;
2389
1974
  return false;
2390
1975
  }
2391
1976
  function portableFrontmatter(skill) {
@@ -2401,9 +1986,9 @@ function portableFrontmatter(skill) {
2401
1986
  };
2402
1987
  }
2403
1988
  async function savePortableSkill(dir, skill) {
2404
- const packageDir = join6(dir, skill.metadata.id);
1989
+ const packageDir = join5(dir, skill.metadata.id);
2405
1990
  await mkdir4(packageDir, { recursive: true });
2406
- await writeFile5(join6(packageDir, "SKILL.md"), matter3.stringify(skill.prompt, portableFrontmatter(skill)), "utf-8");
1991
+ await writeFile5(join5(packageDir, "SKILL.md"), matter3.stringify(skill.prompt, portableFrontmatter(skill)), "utf-8");
2407
1992
  }
2408
1993
  async function saveSkill(configDir, skill) {
2409
1994
  await savePortableSkill(getSkillsDir(configDir), skill);
@@ -2486,7 +2071,7 @@ async function deleteSkill(configDir, skillId) {
2486
2071
  if (isDefault) {
2487
2072
  return { success: false, reason: "Cannot delete built-in defaults" };
2488
2073
  }
2489
- const filePath = join6(getSkillsDir(configDir), `${skillId}${SKILL_EXTENSION}`);
2074
+ const filePath = join5(getSkillsDir(configDir), `${skillId}${SKILL_EXTENSION}`);
2490
2075
  try {
2491
2076
  await unlink3(filePath);
2492
2077
  deleteSetting(`${SKILL_SETTING_PREFIX}${skillId}`);
@@ -2522,6 +2107,21 @@ function createToolProgressHandler(append, messageId, callId, _sessionId) {
2522
2107
  // src/server/chat/execute-tools.ts
2523
2108
  import stripAnsi2 from "strip-ansi";
2524
2109
  var INTERRUPTED_ERROR = "Tool execution was interrupted by user";
2110
+ function extractSubAgentPrompt(args) {
2111
+ return args["prompt"] || args["query"] || args["task"] || "";
2112
+ }
2113
+ async function transformSubAgentAliases(toolCalls, toolRegistry) {
2114
+ const hasCallSubAgent = toolRegistry.tools.some((t) => t.name === "call_sub_agent");
2115
+ if (!hasCallSubAgent) return;
2116
+ const agents = await loadAllAgentsDefault();
2117
+ for (const tc of toolCalls) {
2118
+ const agentDef = findAgentById(tc.name, agents);
2119
+ if (!agentDef?.metadata.subagent) continue;
2120
+ const prompt = extractSubAgentPrompt(tc.arguments);
2121
+ tc.name = "call_sub_agent";
2122
+ tc.arguments = { subAgentType: agentDef.metadata.id, prompt };
2123
+ }
2124
+ }
2525
2125
  function createInterruptedResult(startTime) {
2526
2126
  return {
2527
2127
  success: false,
@@ -2538,6 +2138,7 @@ async function executeTools(assistantMsgId, toolCalls, ctx, append) {
2538
2138
  if (ctx.signal?.aborted) {
2539
2139
  throw new Error("Aborted");
2540
2140
  }
2141
+ await transformSubAgentAliases(toolCalls, ctx.toolRegistry);
2541
2142
  for (const toolCall of toolCalls) {
2542
2143
  append(createToolCallEvent(assistantMsgId, toolCall));
2543
2144
  }
@@ -2843,6 +2444,8 @@ ${CONTINUE_PROMPT}` : CONTINUE_PROMPT;
2843
2444
  const requestedMaxTokens = modelSettings.maxTokens ?? 16384;
2844
2445
  modelSettings = { ...modelSettings, maxTokens: Math.min(requestedMaxTokens, availableForOutput) };
2845
2446
  }
2447
+ const allAgents = await loadAllAgentsDefault();
2448
+ const subAgentAliases = new Set(getSubAgents(allAgents).map((a) => a.metadata.id));
2846
2449
  const streamGen = streamLLMPure({
2847
2450
  messageId: assistantMsgId,
2848
2451
  systemPrompt: assembledRequest.systemPrompt,
@@ -2851,6 +2454,7 @@ ${CONTINUE_PROMPT}` : CONTINUE_PROMPT;
2851
2454
  tools: assembledRequest.tools,
2852
2455
  toolChoice: "auto",
2853
2456
  signal,
2457
+ subAgentAliases,
2854
2458
  ...config.retryPatterns ? { retryPatterns: config.retryPatterns } : {},
2855
2459
  ...modelSettings && { modelSettings }
2856
2460
  });
@@ -2909,7 +2513,7 @@ ${CONTINUE_PROMPT}` : CONTINUE_PROMPT;
2909
2513
  sessionManager.setCurrentContextSize(sessionId, result.usage.promptTokens, config.subAgentMetadata?.subAgentId);
2910
2514
  if (!compacting) {
2911
2515
  const contextState2 = sessionManager.getContextState(sessionId);
2912
- const { shouldCompact, appendCompactionPrompt } = await import("./compactor-4UKYWC5F.js");
2516
+ const { shouldCompact, appendCompactionPrompt } = await import("./compactor-BB3YYVRP.js");
2913
2517
  if (shouldCompact(contextState2.currentTokens, contextState2.maxTokens, runtimeConfig.context.compactionThreshold)) {
2914
2518
  appendCompactionPrompt(sessionId, append);
2915
2519
  compacting = true;
@@ -3609,7 +3213,7 @@ async function loadGitIgnoreRules(workdir) {
3609
3213
  if (!isAbsolute3(workdir)) return "";
3610
3214
  let currentDir = workdir;
3611
3215
  while (true) {
3612
- const gitignorePath = join7(currentDir, ".gitignore");
3216
+ const gitignorePath = join6(currentDir, ".gitignore");
3613
3217
  try {
3614
3218
  await access5(gitignorePath);
3615
3219
  const content = await readFile8(gitignorePath, "utf-8");
@@ -3814,7 +3418,7 @@ var callSubAgentTool = {
3814
3418
  };
3815
3419
  }
3816
3420
  try {
3817
- const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-APW7MY4E.js");
3421
+ const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-JRCUWPJO.js");
3818
3422
  const toolRegistry = getToolRegistryForAgent2(agentDef);
3819
3423
  const turnMetrics = new TurnMetrics();
3820
3424
  const result = await executeSubAgent({
@@ -4100,7 +3704,7 @@ var webFetchTool = createTool(
4100
3704
  // src/server/dev-server/manager.ts
4101
3705
  import "child_process";
4102
3706
  import { readFile as readFile9, writeFile as writeFile6, mkdir as mkdir5 } from "fs/promises";
4103
- import { resolve as resolve5, join as join8 } from "path";
3707
+ import { resolve as resolve4, join as join7 } from "path";
4104
3708
  import net from "net";
4105
3709
  var MAX_LOG_LINES = 2e3;
4106
3710
  var MAX_LOG_BYTES = 1e5;
@@ -4108,7 +3712,7 @@ var DEFAULT_PROBE_PORT = 10469;
4108
3712
  var MAX_PORT_SCAN = 200;
4109
3713
  var PROBE_TIMEOUT_MS = 300;
4110
3714
  var getDevServerConfigPath = (workdir) => {
4111
- return join8(resolve5(workdir), ".openfox", "dev.json");
3715
+ return join7(resolve4(workdir), ".openfox", "dev.json");
4112
3716
  };
4113
3717
  var ERROR_PATTERNS = [
4114
3718
  /EADDRINUSE/,
@@ -4157,7 +3761,7 @@ var DevServerManager = class {
4157
3761
  this._sessionManager = sm;
4158
3762
  }
4159
3763
  resolveWorkdir(workdir) {
4160
- return resolve5(workdir);
3764
+ return resolve4(workdir);
4161
3765
  }
4162
3766
  getInstance(workdir) {
4163
3767
  const key = this.resolveWorkdir(workdir);
@@ -4255,7 +3859,7 @@ var DevServerManager = class {
4255
3859
  }
4256
3860
  async saveConfig(workdir, config) {
4257
3861
  const resolved = this.resolveWorkdir(workdir);
4258
- const dirPath = join8(resolved, ".openfox");
3862
+ const dirPath = join7(resolved, ".openfox");
4259
3863
  await mkdir5(dirPath, { recursive: true });
4260
3864
  const configPath = getDevServerConfigPath(workdir);
4261
3865
  await writeFile6(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
@@ -4813,7 +4417,7 @@ var mcpConfigTool = createTool(
4813
4417
  mcpNotifyChanged?.(sessionId);
4814
4418
  }
4815
4419
  async function rebuildTools() {
4816
- const { setMcpTools: setMcpTools2 } = await import("./tools-APW7MY4E.js");
4420
+ const { setMcpTools: setMcpTools2 } = await import("./tools-JRCUWPJO.js");
4817
4421
  const mcpTools = createMcpTools(mcpManagerForTools);
4818
4422
  setMcpTools2(mcpTools);
4819
4423
  }
@@ -4899,7 +4503,9 @@ var mcpConfigTool = createTool(
4899
4503
  notifyContextChanged(context.sessionId);
4900
4504
  const server = mcpManagerForTools.getServer(args.name);
4901
4505
  const toolCount = server?.tools.length ?? 0;
4902
- return helpers.success(`Updated MCP server "${args.name}" (${toolCount} tools discovered). ${APPLY_PROMPT_MESSAGE}`);
4506
+ return helpers.success(
4507
+ `Updated MCP server "${args.name}" (${toolCount} tools discovered). ${APPLY_PROMPT_MESSAGE}`
4508
+ );
4903
4509
  }
4904
4510
  if (args.action === "remove") {
4905
4511
  if (!args.name) return helpers.error("Missing required field: name");
@@ -5243,39 +4849,11 @@ function validateToolAction(toolName, action, permissions) {
5243
4849
  }
5244
4850
  return void 0;
5245
4851
  }
5246
- function extractSubAgentPrompt(args) {
5247
- return args["prompt"] || args["query"] || args["task"] || "";
5248
- }
5249
4852
  function createRegistryFromTools(tools, allowedTools, toolPermissions, agentId, isSubAgent) {
5250
4853
  const toolMap = /* @__PURE__ */ new Map();
5251
4854
  for (const tool of tools) {
5252
4855
  toolMap.set(tool.name, tool);
5253
4856
  }
5254
- async function trySubAgentAlias(name, args, context) {
5255
- if (!toolMap.has("call_sub_agent")) {
5256
- return null;
5257
- }
5258
- try {
5259
- const agents = await loadAllAgentsDefault();
5260
- const agentDef = findAgentById(name, agents);
5261
- if (!agentDef?.metadata.subagent) {
5262
- return null;
5263
- }
5264
- const prompt = extractSubAgentPrompt(args);
5265
- logger.info("Sub-agent alias transformation", {
5266
- from: name,
5267
- to: "call_sub_agent",
5268
- subAgentType: name
5269
- });
5270
- return callSubAgentTool.execute({ subAgentType: name, prompt }, context);
5271
- } catch (err) {
5272
- logger.warn("Sub-agent alias resolution failed", {
5273
- tool: name,
5274
- error: err instanceof Error ? err.message : String(err)
5275
- });
5276
- return null;
5277
- }
5278
- }
5279
4857
  return {
5280
4858
  tools,
5281
4859
  definitions: tools.map((t) => t.definition),
@@ -5351,8 +4929,6 @@ function createRegistryFromTools(tools, allowedTools, toolPermissions, agentId,
5351
4929
  };
5352
4930
  }
5353
4931
  }
5354
- const aliasResult = await trySubAgentAlias(name, args, context);
5355
- if (aliasResult) return aliasResult;
5356
4932
  return {
5357
4933
  success: false,
5358
4934
  error: `Unknown tool: ${name}. Available tools: ${tools.map((t) => t.name).join(", ")}`,
@@ -5429,10 +5005,6 @@ function createToolRegistry() {
5429
5005
  }
5430
5006
 
5431
5007
  export {
5432
- requestPathAccess,
5433
- PathAccessDeniedError,
5434
- providePathConfirmation,
5435
- cancelPathConfirmationsForSession,
5436
5008
  pathExists,
5437
5009
  getDefaultIds,
5438
5010
  loadItemsFromDir,
@@ -5493,4 +5065,4 @@ export {
5493
5065
  getToolRegistryForAgent,
5494
5066
  createToolRegistry
5495
5067
  };
5496
- //# sourceMappingURL=chunk-2VBETAU2.js.map
5068
+ //# sourceMappingURL=chunk-D6CI2B7B.js.map