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
package/README.md CHANGED
@@ -85,6 +85,8 @@ openfox provider remove # Remove provider
85
85
 
86
86
  OpenFox supports provider plugins loaded from the `plugins` directory inside its configuration folder. Plugins can add authentication methods, transports, provider presets, and model discovery without adding provider-specific code to the OpenFox core.
87
87
 
88
+ You can install the plugin manually or directly from the UI.
89
+
88
90
  Default plugin directories:
89
91
 
90
92
  - macOS: `~/Library/Application Support/openfox/plugins`
@@ -93,8 +95,9 @@ Default plugin directories:
93
95
 
94
96
  Restart OpenFox after installing or updating a plugin.
95
97
 
96
- To authenticate with a ChatGPT Plus or Pro account, you can install the [`openfox-chatgpt`](https://github.com/arthurlacoste/openfox-chatgpt) plugin.
97
- To authenticate with a Github copilot account, you can install the [`openfox-github-copilot`](https://github.com/JamesDAdams/openfox-github-copilot) plugin.
98
+ - To authenticate with a ChatGPT Plus or Pro account, you can install the [`openfox-chatgpt`](https://github.com/arthurlacoste/openfox-chatgpt) plugin.
99
+ - To authenticate with a Github copilot account, you can install the [`openfox-github-copilot`](https://github.com/JamesDAdams/openfox-github-copilot) plugin.
100
+ - To authenticate with a Google Antigravity account, you can install the [`openfox-google-antigravity`](https://github.com/JamesDAdams/openfox-google-antigravity) plugin.
98
101
 
99
102
  ## Screenshots
100
103
 
@@ -2,39 +2,40 @@ import {
2
2
  buildRunChatTurnParams,
3
3
  finalizeTurnCompletion,
4
4
  generateSessionNameForSession
5
- } from "./chunk-2DCJERYX.js";
5
+ } from "./chunk-XTK6E2QR.js";
6
6
  import {
7
7
  runChatTurn
8
- } from "./chunk-ZRQDCQ7R.js";
9
- import "./chunk-2VBETAU2.js";
8
+ } from "./chunk-HLHGNHMV.js";
9
+ import "./chunk-D6CI2B7B.js";
10
+ import "./chunk-NWO6GRYE.js";
11
+ import "./chunk-KVRSAC4Z.js";
10
12
  import "./chunk-MF3UW36X.js";
11
13
  import "./chunk-WGMJCFCE.js";
12
14
  import "./chunk-EU3WWTFH.js";
13
- import "./chunk-XWBNYU2L.js";
14
- import "./chunk-E5LLEO3C.js";
15
- import "./chunk-UV3EMWL4.js";
16
- import "./chunk-PYB2UEC3.js";
15
+ import "./chunk-7772ENZN.js";
16
+ import "./chunk-DH3SSBZN.js";
17
+ import "./chunk-LXFLRM7M.js";
18
+ import "./chunk-OBQPXUXI.js";
19
+ import "./chunk-4QHDQP2N.js";
20
+ import "./chunk-YMFCFMMB.js";
17
21
  import "./chunk-I72ACQ6R.js";
18
- import "./chunk-NWO6GRYE.js";
19
- import "./chunk-UN26GIQG.js";
22
+ import "./chunk-46RORSKU.js";
20
23
  import {
21
24
  getEventStore
22
- } from "./chunk-6XKM25OT.js";
25
+ } from "./chunk-NGJ5XJ7D.js";
26
+ import "./chunk-7QXRQ5ZL.js";
23
27
  import "./chunk-DJASQ2ZB.js";
24
28
  import {
25
29
  createChatMessageMessage,
26
30
  createPhaseChangedMessage,
27
31
  createSessionRunningMessage
28
- } from "./chunk-NOU2QIL5.js";
29
- import "./chunk-S7O77CVO.js";
30
- import "./chunk-QNKXZH3Z.js";
31
- import "./chunk-I4RWK2L2.js";
32
+ } from "./chunk-O3NUNU4H.js";
32
33
  import "./chunk-J2GP3J3X.js";
33
34
  import "./chunk-V4IE7HJY.js";
34
35
  import "./chunk-YHEQTVFV.js";
35
36
  import "./chunk-EZK7CGJ7.js";
36
37
  import "./chunk-K44MW7JJ.js";
37
- import "./chunk-YD6NDTKF.js";
38
+ import "./chunk-5GOOSZX7.js";
38
39
  import "./chunk-WGAAQASA.js";
39
40
  import "./chunk-CQGTEGKL.js";
40
41
  import "./chunk-5WRI5ZAA.js";
@@ -158,4 +159,4 @@ export {
158
159
  startChatSession,
159
160
  stopSessionExecution
160
161
  };
161
- //# sourceMappingURL=chat-handler-ETKFIHUO.js.map
162
+ //# sourceMappingURL=chat-handler-3BZ4PDL2.js.map
@@ -0,0 +1,495 @@
1
+ import {
2
+ getEventStore
3
+ } from "./chunk-NGJ5XJ7D.js";
4
+ import {
5
+ createChatPathConfirmationMessage
6
+ } from "./chunk-O3NUNU4H.js";
7
+
8
+ // src/server/tools/path-security.ts
9
+ import { realpath } from "fs/promises";
10
+ import { resolve, normalize, join, basename, sep, posix, win32 } from "path";
11
+ import { homedir, tmpdir } from "os";
12
+ var SAFE_PATHS = /* @__PURE__ */ new Set([
13
+ "/dev/null",
14
+ "/dev/zero",
15
+ "/dev/random",
16
+ "/dev/urandom",
17
+ "/dev/stdin",
18
+ "/dev/stdout",
19
+ "/dev/stderr",
20
+ "/dev/fd/0",
21
+ "/dev/fd/1",
22
+ "/dev/fd/2"
23
+ ]);
24
+ var ALLOWED_ROOTS = ["/tmp", "/var/tmp", tmpdir()];
25
+ var SENSITIVE_FILE_PATTERNS = [
26
+ // Dotenv files (but not .envrc, .env-example, .env.example)
27
+ /^\.env$/,
28
+ // .env
29
+ /^\.env\.(?!example)[a-zA-Z0-9_.-]+$/,
30
+ // .env.local, .env.production (not .env.example)
31
+ // Credential files
32
+ /^credentials\.json$/i,
33
+ /^secrets?\.(?:json|ya?ml|toml)$/i,
34
+ // Private keys
35
+ /\.pem$/i,
36
+ /\.key$/i,
37
+ /^id_rsa/,
38
+ /^id_ed25519/,
39
+ /^id_ecdsa/,
40
+ /^id_dsa/,
41
+ // Auth configs
42
+ /^\.netrc$/
43
+ ];
44
+ function isSensitivePath(path) {
45
+ const fileName = basename(path);
46
+ if (!fileName) return false;
47
+ return SENSITIVE_FILE_PATTERNS.some((pattern) => pattern.test(fileName));
48
+ }
49
+ var sessionAllowedPaths = /* @__PURE__ */ new Map();
50
+ function addAllowedPath(sessionId, path) {
51
+ if (!sessionAllowedPaths.has(sessionId)) {
52
+ sessionAllowedPaths.set(sessionId, /* @__PURE__ */ new Set());
53
+ }
54
+ sessionAllowedPaths.get(sessionId).add(normalize(path));
55
+ }
56
+ function addAllowedPaths(sessionId, paths) {
57
+ for (const path of paths) {
58
+ addAllowedPath(sessionId, path);
59
+ }
60
+ }
61
+ function isPathAllowed(sessionId, path) {
62
+ const allowed = sessionAllowedPaths.get(sessionId);
63
+ if (!allowed) return false;
64
+ return allowed.has(normalize(path));
65
+ }
66
+ function clearAllowedPaths(sessionId) {
67
+ sessionAllowedPaths.delete(sessionId);
68
+ }
69
+ async function safeRealpath(path) {
70
+ const absolutePath = normalize(resolve(path));
71
+ try {
72
+ return await realpath(absolutePath);
73
+ } catch {
74
+ const missingSegments = [];
75
+ let current = absolutePath;
76
+ while (true) {
77
+ try {
78
+ const canonicalParent = await realpath(current);
79
+ return normalize(join(canonicalParent, ...missingSegments.reverse()));
80
+ } catch {
81
+ const parent = resolve(current, "..");
82
+ if (parent === current) return absolutePath;
83
+ missingSegments.push(basename(current));
84
+ current = parent;
85
+ }
86
+ }
87
+ }
88
+ }
89
+ async function isPathWithinSandbox(path, workdir, sessionId) {
90
+ const normalizedWorkdir = normalize((await safeRealpath(workdir)).replace(/\/+$/, ""));
91
+ let resolvedPath;
92
+ try {
93
+ resolvedPath = normalize(await realpath(path));
94
+ } catch {
95
+ try {
96
+ const { readlink } = await import("fs/promises");
97
+ const linkTarget = await readlink(path);
98
+ const linkDir = resolve(path, "..");
99
+ resolvedPath = await safeRealpath(resolve(linkDir, linkTarget));
100
+ } catch {
101
+ resolvedPath = await safeRealpath(path);
102
+ }
103
+ }
104
+ resolvedPath = resolvedPath.replace(/\/+$/, "");
105
+ if (resolvedPath === normalizedWorkdir || resolvedPath.startsWith(normalizedWorkdir + sep)) {
106
+ return { allowed: true, resolvedPath };
107
+ }
108
+ for (const root of ALLOWED_ROOTS) {
109
+ const normalizedRoot = normalize((await safeRealpath(root)).replace(/\/+$/, ""));
110
+ if (resolvedPath === normalizedRoot || resolvedPath.startsWith(normalizedRoot + sep)) {
111
+ return { allowed: true, resolvedPath };
112
+ }
113
+ }
114
+ if (sessionId && (isPathAllowed(sessionId, path) || isPathAllowed(sessionId, resolvedPath))) {
115
+ return { allowed: true, resolvedPath };
116
+ }
117
+ return { allowed: false, resolvedPath };
118
+ }
119
+ function looksLikeRegex(str) {
120
+ return /[*?+[\]\\]/.test(str);
121
+ }
122
+ var isWindows = () => process.platform === "win32";
123
+ function isWindowsAbsolutePath(str) {
124
+ return /^[A-Za-z]:[\\/]/.test(str);
125
+ }
126
+ function normalizeExtracted(path) {
127
+ return isWindowsAbsolutePath(path) ? win32.normalize(path) : posix.normalize(path);
128
+ }
129
+ function isPlaceholderToken(str) {
130
+ return str.includes("__URL__") || str.includes("__FILEURL__") || str.includes("__SED__") || str.includes("__COMMIT_MSG__");
131
+ }
132
+ function extractAbsolutePathsFromCommand(command) {
133
+ if (!command.trim()) {
134
+ return [];
135
+ }
136
+ const paths = [];
137
+ const home = homedir();
138
+ let sanitized = command.replace(/https?:\/\/[^\s'"]+/g, " __URL__ ").replace(/ftp:\/\/[^\s'"]+/g, " __URL__ ");
139
+ sanitized = sanitized.replace(/(?<!\w)s\/[^/]*\/[^/]*\/[gip]*/g, " __SED__ ");
140
+ sanitized = sanitized.replace(/(?<!\w)s\|[^|]*\|[^|]*\|[gip]*/g, " __SED__ ");
141
+ sanitized = sanitized.replace(/(?<!\w)s:[^:]*:[^:]*:[gip]*/g, " __SED__ ");
142
+ sanitized = sanitized.replace(
143
+ /git\s+commit\b.*?(?:-(?:[a-zA-Z]*m)(?=\s)|--message)\s+(["'])(?:(?!\1).)*\1/g,
144
+ (match2) => match2.replace(/\/[^\s"'|&;<>`()]+/g, " __COMMIT_MSG__ ")
145
+ );
146
+ const fileUrlMatches = command.matchAll(/file:\/\/([^\s'"]+)/g);
147
+ for (const match2 of fileUrlMatches) {
148
+ const filePath = match2[1];
149
+ if (filePath && !isSafePath(filePath)) {
150
+ paths.push(normalizeExtracted(filePath));
151
+ }
152
+ }
153
+ sanitized = sanitized.replace(/file:\/\/[^\s'"]+/g, " __FILEURL__ ");
154
+ const tildePattern = /(?:^|[\s='"(])~(\/[^\s'"()]*)?(?=[\s'"()]|$)/g;
155
+ let match;
156
+ while ((match = tildePattern.exec(sanitized)) !== null) {
157
+ const pathPart = match[1] ?? "";
158
+ const fullPath = home + pathPart;
159
+ const resolved = normalize(resolve(fullPath));
160
+ if (!isSafePath(resolved)) {
161
+ paths.push(resolved);
162
+ }
163
+ }
164
+ const quotedPattern = /["']([^"']+)["']/g;
165
+ while ((match = quotedPattern.exec(sanitized)) !== null) {
166
+ const content = match[1];
167
+ if (content.startsWith("/") && content.endsWith("/")) {
168
+ continue;
169
+ }
170
+ if (content.startsWith("/") && looksLikeRegex(content)) {
171
+ continue;
172
+ }
173
+ if (isWindowsAbsolutePath(content)) {
174
+ const resolved = normalizeExtracted(content);
175
+ if (!isSafePath(resolved)) {
176
+ paths.push(resolved);
177
+ }
178
+ } else if (!isWindows() && content.startsWith("/")) {
179
+ const resolved = normalizeExtracted(content);
180
+ if (!isSafePath(resolved)) {
181
+ paths.push(resolved);
182
+ }
183
+ } else if (content.startsWith("~")) {
184
+ const pathPart = content.slice(1);
185
+ const fullPath = join(home, pathPart);
186
+ const resolved = normalize(resolve(fullPath));
187
+ if (!isSafePath(resolved)) {
188
+ paths.push(resolved);
189
+ }
190
+ }
191
+ }
192
+ if (isWindows()) {
193
+ const winAbsolutePattern = /(?:^|[\s=(])([A-Za-z]:[\\/][^\s"'|&;<>`()]+)/g;
194
+ while ((match = winAbsolutePattern.exec(sanitized)) !== null) {
195
+ const candidate = match[1];
196
+ if (isPlaceholderToken(candidate)) continue;
197
+ const resolved = normalizeExtracted(candidate);
198
+ if (!isSafePath(resolved)) {
199
+ paths.push(resolved);
200
+ }
201
+ }
202
+ } else {
203
+ const absolutePattern = /(?:^|[\s=(])(\/[^\s"'|&;<>`()]+)/g;
204
+ while ((match = absolutePattern.exec(sanitized)) !== null) {
205
+ const candidate = match[1];
206
+ if (isPlaceholderToken(candidate)) continue;
207
+ if (looksLikeRegex(candidate)) continue;
208
+ const resolved = normalizeExtracted(candidate);
209
+ if (resolved === "/" || resolved === "") continue;
210
+ if (!isSafePath(resolved)) {
211
+ paths.push(resolved);
212
+ }
213
+ }
214
+ }
215
+ return [...new Set(paths)];
216
+ }
217
+ function extractSensitivePathsFromCommand(command) {
218
+ if (!command.trim()) {
219
+ return [];
220
+ }
221
+ const paths = [];
222
+ const quotedPattern = /["']([^"']+)["']/g;
223
+ let match;
224
+ while ((match = quotedPattern.exec(command)) !== null) {
225
+ const content = match[1];
226
+ if (isSensitivePath(content)) {
227
+ paths.push(content);
228
+ }
229
+ }
230
+ const unquoted = command.replace(/["'][^"']*["']/g, " ").replace(/[|&;><]/g, " ");
231
+ const tokens = unquoted.split(/\s+/).filter(Boolean);
232
+ for (const token of tokens) {
233
+ if (token.startsWith("-")) continue;
234
+ if (token.includes("://")) continue;
235
+ if (isSensitivePath(token)) {
236
+ paths.push(token);
237
+ }
238
+ }
239
+ return [...new Set(paths)];
240
+ }
241
+ function isSafePath(path) {
242
+ const normalized = posix.normalize(path);
243
+ return SAFE_PATHS.has(normalized);
244
+ }
245
+ async function checkPathsAccess(paths, workdir, sessionId) {
246
+ if (paths.length === 0) {
247
+ return { needsConfirmation: false, deniedPaths: [], sensitivePaths: [] };
248
+ }
249
+ const deniedPaths = [];
250
+ const sensitivePaths = [];
251
+ for (const path of paths) {
252
+ const result = await isPathWithinSandbox(path, workdir, sessionId);
253
+ if (!result.allowed) {
254
+ deniedPaths.push(result.resolvedPath);
255
+ }
256
+ if (isSensitivePath(path) && !isPathAllowed(sessionId ?? "", path)) {
257
+ sensitivePaths.push(result.resolvedPath);
258
+ }
259
+ }
260
+ const uniqueDenied = [...new Set(deniedPaths)];
261
+ const uniqueSensitive = [...new Set(sensitivePaths)];
262
+ return {
263
+ needsConfirmation: uniqueDenied.length > 0 || uniqueSensitive.length > 0,
264
+ deniedPaths: uniqueDenied,
265
+ sensitivePaths: uniqueSensitive
266
+ };
267
+ }
268
+ var DANGEROUS_PATTERNS = [
269
+ /sudo\s/,
270
+ /rm\s+(-rf?|--recursive)\s+[~]/,
271
+ /chmod\s+777/,
272
+ />\s*\/dev\/sd/,
273
+ /mkfs\s/,
274
+ /dd\s+if=/,
275
+ /:\(\)\s*\{\s*:\s*\|\s*:\s*&\s*\}\s*;/
276
+ ];
277
+ function extractDangerousPatterns(command) {
278
+ const dangerous = [];
279
+ for (const pattern of DANGEROUS_PATTERNS) {
280
+ if (pattern.test(command)) {
281
+ dangerous.push(pattern.source);
282
+ }
283
+ }
284
+ return dangerous;
285
+ }
286
+ function extractGitNoVerify(command) {
287
+ const subCommands = command.split(/\s*(?:&&|\|\||\||;)\s*/);
288
+ for (const sub of subCommands) {
289
+ const parts = sub.trim().split(/\s+/);
290
+ const gitIndex = parts.indexOf("git");
291
+ const subCmd = parts[gitIndex + 1];
292
+ if (gitIndex >= 0 && subCmd && !subCmd.startsWith("-")) {
293
+ const gitArgs = parts.slice(gitIndex + 2);
294
+ if (gitArgs.some((a) => a === "--no-verify" || a === "-n")) {
295
+ return true;
296
+ }
297
+ }
298
+ }
299
+ return false;
300
+ }
301
+ async function requestPathAccess(paths, workdir, sessionId, callId, tool, onEvent, dangerLevel, command, isSubAgent) {
302
+ if (isSubAgent) {
303
+ const result2 = await checkPathsAccess(paths, workdir, sessionId);
304
+ if (!result2.needsConfirmation) return;
305
+ if (dangerLevel === "dangerous") {
306
+ const allPaths2 = [.../* @__PURE__ */ new Set([...result2.deniedPaths, ...result2.sensitivePaths])];
307
+ addAllowedPaths(sessionId, allPaths2);
308
+ return;
309
+ }
310
+ const allPaths = [.../* @__PURE__ */ new Set([...result2.deniedPaths, ...result2.sensitivePaths])];
311
+ const hasDenied2 = result2.deniedPaths.length > 0;
312
+ const hasSensitive2 = result2.sensitivePaths.length > 0;
313
+ const reason2 = hasDenied2 && hasSensitive2 ? "both" : hasDenied2 ? "outside_workdir" : "sensitive_file";
314
+ throw new PathAccessDeniedError(allPaths, tool, reason2);
315
+ }
316
+ const emitPendingEvent = (confirmationPaths, confirmationReason) => {
317
+ try {
318
+ const eventStore = getEventStore();
319
+ eventStore.append(sessionId, {
320
+ type: "path.confirmation_pending",
321
+ data: { callId, tool, paths: confirmationPaths, workdir, reason: confirmationReason }
322
+ });
323
+ } catch {
324
+ }
325
+ };
326
+ if (command && extractGitNoVerify(command)) {
327
+ emitPendingEvent([workdir], "git_no_verify");
328
+ const confirmationPromise2 = registerPathConfirmation(callId, [workdir], sessionId, tool, workdir, "git_no_verify");
329
+ onEvent(createChatPathConfirmationMessage(callId, tool, ["git --no-verify detected"], workdir, "git_no_verify"));
330
+ const approved2 = await confirmationPromise2;
331
+ if (!approved2) {
332
+ throw new PathAccessDeniedError(
333
+ ["git --no-verify"],
334
+ tool,
335
+ "git_no_verify",
336
+ "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."
337
+ );
338
+ }
339
+ }
340
+ if (dangerLevel !== "dangerous" && command) {
341
+ const dangerousPatterns = extractDangerousPatterns(command);
342
+ if (dangerousPatterns.length > 0) {
343
+ emitPendingEvent([workdir], "dangerous_command");
344
+ const confirmationPromise2 = registerPathConfirmation(
345
+ callId,
346
+ [workdir],
347
+ sessionId,
348
+ tool,
349
+ workdir,
350
+ "dangerous_command"
351
+ );
352
+ onEvent(
353
+ createChatPathConfirmationMessage(callId, tool, [dangerousPatterns.join(", ")], workdir, "dangerous_command")
354
+ );
355
+ const approved2 = await confirmationPromise2;
356
+ if (!approved2) {
357
+ throw new PathAccessDeniedError(dangerousPatterns, tool, "dangerous_command");
358
+ }
359
+ }
360
+ }
361
+ const result = await checkPathsAccess(paths, workdir, sessionId);
362
+ if (!result.needsConfirmation) {
363
+ return;
364
+ }
365
+ if (dangerLevel === "dangerous") {
366
+ const allPaths = [.../* @__PURE__ */ new Set([...result.deniedPaths, ...result.sensitivePaths])];
367
+ addAllowedPaths(sessionId, allPaths);
368
+ return;
369
+ }
370
+ const allPathsNeedingConfirmation = [.../* @__PURE__ */ new Set([...result.deniedPaths, ...result.sensitivePaths])];
371
+ const hasDenied = result.deniedPaths.length > 0;
372
+ const hasSensitive = result.sensitivePaths.length > 0;
373
+ const reason = hasDenied && hasSensitive ? "both" : hasDenied ? "outside_workdir" : "sensitive_file";
374
+ emitPendingEvent(allPathsNeedingConfirmation, reason);
375
+ const confirmationPromise = registerPathConfirmation(
376
+ callId,
377
+ allPathsNeedingConfirmation,
378
+ sessionId,
379
+ tool,
380
+ workdir,
381
+ reason
382
+ );
383
+ onEvent(createChatPathConfirmationMessage(callId, tool, allPathsNeedingConfirmation, workdir, reason));
384
+ const approved = await confirmationPromise;
385
+ if (!approved) {
386
+ throw new PathAccessDeniedError(allPathsNeedingConfirmation, tool, reason);
387
+ }
388
+ }
389
+ var PathAccessDeniedError = class extends Error {
390
+ constructor(paths, tool, reason = "outside_workdir", customMessage) {
391
+ 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";
392
+ super(customMessage ?? `User denied access to ${reasonText}: ${paths.join(", ")}`);
393
+ this.paths = paths;
394
+ this.tool = tool;
395
+ this.reason = reason;
396
+ this.customMessage = customMessage;
397
+ this.name = "PathAccessDeniedError";
398
+ }
399
+ };
400
+ var pendingConfirmations = /* @__PURE__ */ new Map();
401
+ function registerPathConfirmation(callId, paths, sessionId, tool, workdir, reason) {
402
+ return new Promise((resolve2, reject) => {
403
+ pendingConfirmations.set(callId, { resolve: resolve2, reject, paths, sessionId, tool, workdir, reason });
404
+ });
405
+ }
406
+ function providePathConfirmation(callId, approved, alwaysAllow) {
407
+ const pending = pendingConfirmations.get(callId);
408
+ if (!pending) {
409
+ return { found: false };
410
+ }
411
+ try {
412
+ const eventStore = getEventStore();
413
+ eventStore.append(pending.sessionId, {
414
+ type: "path.confirmation_responded",
415
+ data: { callId, approved, alwaysAllow: alwaysAllow ?? false }
416
+ });
417
+ } catch {
418
+ }
419
+ if (approved) {
420
+ if (alwaysAllow) {
421
+ addAllowedPaths(pending.sessionId, pending.paths);
422
+ } else {
423
+ addAllowedPaths(pending.sessionId, pending.paths);
424
+ }
425
+ }
426
+ pending.resolve(approved);
427
+ pendingConfirmations.delete(callId);
428
+ return { found: true, sessionId: pending.sessionId, approved };
429
+ }
430
+ function cancelPathConfirmation(callId, reason) {
431
+ const pending = pendingConfirmations.get(callId);
432
+ if (!pending) {
433
+ return false;
434
+ }
435
+ pending.reject(new Error(reason));
436
+ pendingConfirmations.delete(callId);
437
+ return true;
438
+ }
439
+ function cancelPathConfirmationsForSession(sessionId, reason) {
440
+ let cancelledCount = 0;
441
+ for (const [callId, pending] of pendingConfirmations.entries()) {
442
+ if (pending.sessionId !== sessionId) {
443
+ continue;
444
+ }
445
+ pending.reject(new Error(reason));
446
+ pendingConfirmations.delete(callId);
447
+ cancelledCount += 1;
448
+ }
449
+ return cancelledCount;
450
+ }
451
+ function hasPendingPathConfirmation(callId) {
452
+ return pendingConfirmations.has(callId);
453
+ }
454
+ function getPendingConfirmationsBySession() {
455
+ const bySession = {};
456
+ for (const [_callId, pending] of pendingConfirmations.entries()) {
457
+ const entry = {
458
+ callId: _callId,
459
+ tool: pending.tool,
460
+ paths: pending.paths,
461
+ workdir: pending.workdir,
462
+ reason: pending.reason
463
+ };
464
+ const existing = bySession[pending.sessionId];
465
+ if (existing) {
466
+ existing.push(entry);
467
+ } else {
468
+ bySession[pending.sessionId] = [entry];
469
+ }
470
+ }
471
+ return bySession;
472
+ }
473
+
474
+ export {
475
+ isSensitivePath,
476
+ addAllowedPath,
477
+ addAllowedPaths,
478
+ isPathAllowed,
479
+ clearAllowedPaths,
480
+ isPathWithinSandbox,
481
+ extractAbsolutePathsFromCommand,
482
+ extractSensitivePathsFromCommand,
483
+ checkPathsAccess,
484
+ extractDangerousPatterns,
485
+ extractGitNoVerify,
486
+ requestPathAccess,
487
+ PathAccessDeniedError,
488
+ registerPathConfirmation,
489
+ providePathConfirmation,
490
+ cancelPathConfirmation,
491
+ cancelPathConfirmationsForSession,
492
+ hasPendingPathConfirmation,
493
+ getPendingConfirmationsBySession
494
+ };
495
+ //# sourceMappingURL=chunk-46RORSKU.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  SETTINGS_KEYS,
3
3
  getSetting
4
- } from "./chunk-QNKXZH3Z.js";
4
+ } from "./chunk-YMFCFMMB.js";
5
5
 
6
6
  // src/server/utils/platform.ts
7
7
  import os from "os";
@@ -63,4 +63,4 @@ export {
63
63
  isWindowsPath,
64
64
  isAbsolutePath
65
65
  };
66
- //# sourceMappingURL=chunk-S7O77CVO.js.map
66
+ //# sourceMappingURL=chunk-4QHDQP2N.js.map
@@ -26,4 +26,4 @@ export {
26
26
  isClientMessage,
27
27
  isServerMessage
28
28
  };
29
- //# sourceMappingURL=chunk-YD6NDTKF.js.map
29
+ //# sourceMappingURL=chunk-5GOOSZX7.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getPlatformShell
3
- } from "./chunk-S7O77CVO.js";
3
+ } from "./chunk-4QHDQP2N.js";
4
4
  import {
5
5
  streamWithSegments
6
6
  } from "./chunk-J2GP3J3X.js";
@@ -419,13 +419,14 @@ async function* streamLLMPure(options) {
419
419
  const fullName = toolNames.get(value.index);
420
420
  if (!seenToolIndices.has(value.index) && fullName) {
421
421
  seenToolIndices.add(value.index);
422
+ const displayName = options.subAgentAliases?.has(fullName) ? "call_sub_agent" : fullName;
422
423
  const accumulatedArgs = toolArgs.get(value.index);
423
424
  yield {
424
425
  type: "tool.preparing",
425
426
  data: {
426
427
  messageId,
427
428
  index: value.index,
428
- name: fullName,
429
+ name: displayName,
429
430
  ...accumulatedArgs ? { arguments: accumulatedArgs } : {}
430
431
  }
431
432
  };
@@ -678,4 +679,4 @@ export {
678
679
  createChatDoneEvent,
679
680
  consumeStreamGenerator
680
681
  };
681
- //# sourceMappingURL=chunk-XWBNYU2L.js.map
682
+ //# sourceMappingURL=chunk-7772ENZN.js.map
@@ -176,8 +176,8 @@ function runMigrations(db2) {
176
176
  if (!columnNames.includes("workspace") && columnNames.includes("worktree")) {
177
177
  logger.info("Migrating sessions table: renaming worktree to workspace");
178
178
  db2.exec(`ALTER TABLE sessions RENAME COLUMN worktree TO workspace`);
179
- }
180
- if (!columnNames.includes("workspace")) {
179
+ columnNames[columnNames.indexOf("worktree")] = "workspace";
180
+ } else if (!columnNames.includes("workspace")) {
181
181
  logger.info("Migrating sessions table: adding workspace column");
182
182
  db2.exec(`ALTER TABLE sessions ADD COLUMN workspace TEXT`);
183
183
  }
@@ -188,4 +188,4 @@ export {
188
188
  initDatabase,
189
189
  getDatabase
190
190
  };
191
- //# sourceMappingURL=chunk-I4RWK2L2.js.map
191
+ //# sourceMappingURL=chunk-7QXRQ5ZL.js.map