jinzd-ai-cli 0.4.307 → 0.4.308

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 (95) hide show
  1. package/README.md +1 -0
  2. package/THIRD_PARTY_NOTICES.md +3 -0
  3. package/dist/{agent-client-6GX6QQDU.js → agent-client-TIEAYZXV.js} +4 -1
  4. package/dist/{auth-PE3Z5OHS.js → auth-QTQNZ6IX.js} +2 -2
  5. package/dist/{batch-HMADXNOD.js → batch-S7XLEEOH.js} +3 -3
  6. package/dist/{chat-index-2D5GK2DB.js → chat-index-7CLR4CBG.js} +4 -3
  7. package/dist/{chat-index-DGUOUUQW.js → chat-index-RLVLQRRC.js} +3 -2
  8. package/dist/{chunk-SJFZPS65.js → chunk-2DYQDLLF.js} +1 -1
  9. package/dist/{chunk-VAV2IJ3R.js → chunk-2MNUMLN4.js} +1 -1
  10. package/dist/chunk-2XMUCC42.js +67 -0
  11. package/dist/{chunk-5GQL4TSZ.js → chunk-3537CQ5M.js} +2 -2
  12. package/dist/{chunk-OAXLMF3U.js → chunk-3YVNDYOY.js} +1 -1
  13. package/dist/{chunk-RYQBBEMM.js → chunk-4PR5AVJ5.js} +115 -19
  14. package/dist/{chunk-GHWZVPUV.js → chunk-6JHACXB3.js} +7 -5
  15. package/dist/{chunk-CIM2PV2F.js → chunk-7OHXFTZO.js} +2 -2
  16. package/dist/{chunk-IUVDZVZ4.js → chunk-AXTLYOA7.js} +24 -17
  17. package/dist/{chunk-XJOK5353.js → chunk-B3MPYI4E.js} +2 -2
  18. package/dist/{chunk-3CKJOTVM.js → chunk-BGZN4K7Q.js} +4 -4
  19. package/dist/chunk-BMLQPSHP.js +69 -0
  20. package/dist/{chunk-NRSYBKWA.js → chunk-C7CKDFJS.js} +1 -1
  21. package/dist/{chunk-NQJSPMY2.js → chunk-E2PNXNKI.js} +4 -4
  22. package/dist/chunk-FVXNEZEW.js +161 -0
  23. package/dist/{chunk-YKJO3SI4.js → chunk-G6DVGQ4I.js} +1 -1
  24. package/dist/{chunk-YFDQIUQ3.js → chunk-HC33C4U3.js} +18 -18
  25. package/dist/chunk-JJFJWD35.js +162 -0
  26. package/dist/{chunk-KRCYR46A.js → chunk-JJNUASMJ.js} +264 -55
  27. package/dist/{chunk-NYNSYLFY.js → chunk-JL44HGZC.js} +1 -1
  28. package/dist/{chunk-NUJTNH76.js → chunk-LBWKMTT3.js} +1 -1
  29. package/dist/{chunk-AHL6KOGM.js → chunk-LR3TGZWQ.js} +2 -2
  30. package/dist/{chunk-WQ3OMEC4.js → chunk-M6HLYG33.js} +1 -1
  31. package/dist/{chunk-I4GQRMFV.js → chunk-M6YV5KSS.js} +6 -4
  32. package/dist/{chunk-3TBXHLSC.js → chunk-NLK45GUS.js} +1 -1
  33. package/dist/{chunk-KBDKKHXB.js → chunk-O6CGOVD4.js} +2 -2
  34. package/dist/chunk-QMFMST2T.js +39 -0
  35. package/dist/{chunk-MI4SFZGX.js → chunk-RMEJR3AW.js} +5 -2
  36. package/dist/{chunk-UFW4S3RU.js → chunk-RVMCY6DP.js} +1 -1
  37. package/dist/{chunk-LF67HWWR.js → chunk-RWRZVQCB.js} +1 -1
  38. package/dist/chunk-SXIJLSVF.js +41 -0
  39. package/dist/chunk-TDQW2YHK.js +65 -0
  40. package/dist/{chunk-L2RJPVHV.js → chunk-TPTFV3Y6.js} +266 -512
  41. package/dist/{chunk-45UVJXFQ.js → chunk-W2UOVGRC.js} +1 -1
  42. package/dist/{chunk-VXEF2SWG.js → chunk-WT252ZDV.js} +2 -2
  43. package/dist/{chunk-4W6VDVQU.js → chunk-WVEIUSXE.js} +246 -56
  44. package/dist/{chunk-AQF6IXSR.js → chunk-YBZM7PHI.js} +4 -4
  45. package/dist/{ci-QD2Q7NWY.js → ci-5HONSUAS.js} +7 -7
  46. package/dist/{ci-format-42XIZO5P.js → ci-format-URUH3ZZ5.js} +2 -2
  47. package/dist/{constants-NMASHFDL.js → constants-BLDBI7IG.js} +1 -1
  48. package/dist/{constants-5CMEFZQB.js → constants-FG27DQMS.js} +1 -1
  49. package/dist/doctor-cli-QQLMNJN7.js +18 -0
  50. package/dist/electron-server.js +886 -828
  51. package/dist/file-checkpoint-64A2AXHE.js +7 -0
  52. package/dist/{file-checkpoint-NKBHGC7L.js → file-checkpoint-VVPUJNCI.js} +2 -1
  53. package/dist/{hub-ZLF6GMQD.js → hub-GGFW3DCC.js} +3 -3
  54. package/dist/{hub-server-CBKCP5VD.js → hub-server-JLC7CNP7.js} +55 -17
  55. package/dist/index.js +42 -40
  56. package/dist/{indexer-P3FPRTPJ.js → indexer-GKIWSSRP.js} +3 -3
  57. package/dist/indexer-VRXDZ74A.js +10 -0
  58. package/dist/{persist-S77WRVLD.js → persist-ZZXRWFBX.js} +4 -4
  59. package/dist/{persistent-memory-GJE7HKVK.js → persistent-memory-UNLCXUWH.js} +3 -3
  60. package/dist/{persistent-memory-MBSBTBGY.js → persistent-memory-YYWIN6BG.js} +3 -3
  61. package/dist/{pr-SXRW24JG.js → pr-YCFGPFI7.js} +7 -7
  62. package/dist/{project-trust-MUG325AW.js → project-trust-6CJB5ZMX.js} +1 -1
  63. package/dist/{project-trust-NKYHL3VZ.js → project-trust-KGG2BWKC.js} +1 -1
  64. package/dist/run-tests-GHY4BYDE.js +13 -0
  65. package/dist/run-tests-KRNMFXAN.js +12 -0
  66. package/dist/{semantic-7GM5M4MU.js → semantic-N227CLSC.js} +4 -4
  67. package/dist/{semantic-QOBAXEQ4.js → semantic-TRQAZSRY.js} +4 -4
  68. package/dist/{server-MO3EU2XD.js → server-73UBHSFV.js} +16 -14
  69. package/dist/{server-GGEO7SA2.js → server-QRAWSG4Z.js} +231 -103
  70. package/dist/{store-SIKHCUYJ.js → store-2N6PPPBP.js} +2 -2
  71. package/dist/{store-CMSSWZF7.js → store-76VVFDXM.js} +2 -2
  72. package/dist/{task-orchestrator-JGEEQ2OE.js → task-orchestrator-GI3RGLZI.js} +15 -14
  73. package/dist/{usage-B7RIAPPT.js → usage-GWTIGZF5.js} +4 -4
  74. package/dist/{vector-store-JAXWDZYW.js → vector-store-T2DVUMY4.js} +2 -2
  75. package/dist/{vector-store-PWIOQFTP.js → vector-store-V6UGEYWK.js} +2 -2
  76. package/dist/web/client/app.js +7 -0
  77. package/dist/web/client/app.js.br +0 -0
  78. package/dist/web/client/app.js.gz +0 -0
  79. package/dist/web/client/util.js +14 -23
  80. package/dist/web/client/util.js.br +0 -0
  81. package/dist/web/client/util.js.gz +0 -0
  82. package/dist/web/client/vendor/DOMPurify-LICENSE.txt +202 -0
  83. package/dist/web/client/vendor/purify.es.mjs +2722 -0
  84. package/dist/web/client/vendor/purify.es.mjs.br +0 -0
  85. package/dist/web/client/vendor/purify.es.mjs.gz +0 -0
  86. package/package.json +4 -2
  87. package/dist/chunk-4BKXL7SM.js +0 -98
  88. package/dist/chunk-6BUTA5VW.js +0 -38
  89. package/dist/chunk-IW3Q7AE5.js +0 -40
  90. package/dist/chunk-SKET65WZ.js +0 -96
  91. package/dist/doctor-cli-IOVSHBDI.js +0 -18
  92. package/dist/file-checkpoint-CGH6OJVI.js +0 -6
  93. package/dist/indexer-FPKY7LCT.js +0 -10
  94. package/dist/run-tests-EQCAMFQE.js +0 -10
  95. package/dist/run-tests-V3N7YV3P.js +0 -9
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  loadChatIndex,
4
4
  searchChatMemory
5
- } from "./chunk-GHWZVPUV.js";
5
+ } from "./chunk-6JHACXB3.js";
6
6
  import {
7
7
  addMemoryEntry,
8
8
  findSimilarMemory,
@@ -11,17 +11,17 @@ import {
11
11
  searchPersistentMemories,
12
12
  touchMemoryReferences,
13
13
  updateMemoryEntry
14
- } from "./chunk-AHL6KOGM.js";
14
+ } from "./chunk-LR3TGZWQ.js";
15
15
  import {
16
16
  indexProject
17
- } from "./chunk-LF67HWWR.js";
17
+ } from "./chunk-RWRZVQCB.js";
18
18
  import {
19
19
  hasSemanticIndex,
20
20
  semanticSearch
21
- } from "./chunk-XJOK5353.js";
21
+ } from "./chunk-B3MPYI4E.js";
22
22
  import {
23
23
  loadIndex
24
- } from "./chunk-WQ3OMEC4.js";
24
+ } from "./chunk-M6HLYG33.js";
25
25
  import {
26
26
  theme
27
27
  } from "./chunk-5C76XDDF.js";
@@ -30,13 +30,13 @@ import {
30
30
  runHook,
31
31
  runLifecycleHooks,
32
32
  runTool
33
- } from "./chunk-5GQL4TSZ.js";
33
+ } from "./chunk-3537CQ5M.js";
34
34
  import {
35
35
  buildTruncatedArgumentsError,
36
36
  getDangerLevel,
37
37
  isFileWriteTool,
38
38
  runLeanAgentLoop
39
- } from "./chunk-MI4SFZGX.js";
39
+ } from "./chunk-RMEJR3AW.js";
40
40
  import {
41
41
  EnvLoader,
42
42
  NetworkError,
@@ -44,13 +44,26 @@ import {
44
44
  } from "./chunk-TU3L3PW7.js";
45
45
  import {
46
46
  atomicWriteFileSync
47
- } from "./chunk-IW3Q7AE5.js";
47
+ } from "./chunk-BMLQPSHP.js";
48
48
  import {
49
- fileCheckpoints
50
- } from "./chunk-4BKXL7SM.js";
49
+ MAX_SNAPSHOT_TOTAL_BYTES,
50
+ assertReadablePath,
51
+ canonicalPath,
52
+ fileCheckpoints,
53
+ isCredentialVault,
54
+ readSnapshot
55
+ } from "./chunk-JJFJWD35.js";
51
56
  import {
52
- runTestsTool
53
- } from "./chunk-4W6VDVQU.js";
57
+ IS_WINDOWS,
58
+ WIN_UTF8_PREAMBLE,
59
+ currentAbortSignal,
60
+ decodeWindowsBuffer,
61
+ killProcessTree,
62
+ looksLikeGbkMojibake,
63
+ recoverGbkMojibake,
64
+ runTestsTool,
65
+ shellInvocation
66
+ } from "./chunk-JJNUASMJ.js";
54
67
  import {
55
68
  CONFIG_DIR_NAME,
56
69
  DEFAULT_MAX_TOOL_OUTPUT_CHARS_CAP,
@@ -59,170 +72,25 @@ import {
59
72
  SUBAGENT_ALLOWED_TOOLS,
60
73
  SUBAGENT_DEFAULT_MAX_ROUNDS,
61
74
  SUBAGENT_MAX_ROUNDS_LIMIT
62
- } from "./chunk-VAV2IJ3R.js";
75
+ } from "./chunk-2MNUMLN4.js";
76
+ import {
77
+ getCurrentSessionKey,
78
+ getExecutionConfigDir,
79
+ runWithSessionKey,
80
+ scopedState
81
+ } from "./chunk-SXIJLSVF.js";
63
82
 
64
83
  // src/tools/shell-pool.ts
65
- import { spawn as spawn2 } from "child_process";
66
- import { platform as platform2 } from "os";
67
-
68
- // src/tools/win-shell.ts
69
- import { platform } from "os";
70
84
  import { spawn } from "child_process";
71
- var IS_WINDOWS = platform() === "win32";
72
- var WIN_UTF8_PREAMBLE = "[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; $OutputEncoding = [System.Text.Encoding]::UTF8; ";
73
- function shellInvocation(command) {
74
- if (IS_WINDOWS) {
75
- return {
76
- shell: "powershell.exe",
77
- args: ["-NoProfile", "-NonInteractive", "-Command", WIN_UTF8_PREAMBLE + command]
78
- };
79
- }
80
- return { shell: process.env["SHELL"] ?? "/bin/bash", args: ["-c", command] };
81
- }
82
- function decodeWindowsBuffer(buf) {
83
- const utf8 = buf.toString("utf-8");
84
- if (!utf8.includes("\uFFFD")) return utf8;
85
- try {
86
- return new TextDecoder("gbk").decode(buf);
87
- } catch {
88
- return utf8;
89
- }
90
- }
91
- var GBK_MOJIBAKE_FINGERPRINTS = [
92
- "\uFFFD",
93
- "\u9215",
94
- "\u951B",
95
- "\u9983",
96
- "\u9241",
97
- "\u6D93",
98
- "\u935B",
99
- "\u9428",
100
- "\u7EFE",
101
- "\u95AB",
102
- "\u9593",
103
- "\u95BF",
104
- "\u68E3",
105
- "\u95B4",
106
- "\u5A11",
107
- "\u95B8",
108
- "\u95BB",
109
- "\u7F01",
110
- "\u95C1"
111
- ];
112
- var gbkReverse = null;
113
- function gbkReverseMap() {
114
- if (gbkReverse) return gbkReverse;
115
- const map = /* @__PURE__ */ new Map();
116
- try {
117
- const dec = new TextDecoder("gbk");
118
- for (let hi = 129; hi <= 254; hi++) {
119
- for (let lo = 64; lo <= 254; lo++) {
120
- if (lo === 127) continue;
121
- const ch = dec.decode(new Uint8Array([hi, lo]));
122
- if (ch.length === 1 && ch !== "\uFFFD" && !map.has(ch)) map.set(ch, [hi, lo]);
123
- }
124
- }
125
- } catch {
126
- }
127
- gbkReverse = map;
128
- return map;
129
- }
130
- function decodeRunAsChinese(bytes) {
131
- for (let start = 0; start <= 2 && start < bytes.length; start++) {
132
- for (let end = 0; end <= 2 && bytes.length - end > start; end++) {
133
- const slice = bytes.slice(start, bytes.length - end);
134
- if (slice.length < 6) continue;
135
- try {
136
- const out = new TextDecoder("utf-8", { fatal: true }).decode(new Uint8Array(slice));
137
- if (/[一-鿿]/.test(out)) return out;
138
- } catch {
139
- }
140
- }
141
- }
142
- return null;
143
- }
144
- function recoverGbkMojibake(text) {
145
- if (!text) return null;
146
- const map = gbkReverseMap();
147
- if (map.size === 0) return null;
148
- let run = [];
149
- let best = null;
150
- const flush = () => {
151
- if (run.length >= 4) {
152
- const bytes = [];
153
- for (const p of run) bytes.push(p[0], p[1]);
154
- const out = decodeRunAsChinese(bytes);
155
- if (out && (!best || out.length > best.length)) best = out;
156
- }
157
- run = [];
158
- };
159
- for (const ch of text) {
160
- const pair = ch.codePointAt(0) < 128 ? void 0 : map.get(ch);
161
- if (pair) run.push(pair);
162
- else flush();
163
- }
164
- flush();
165
- return best;
166
- }
167
- function looksLikeGbkMojibake(text) {
168
- if (recoverGbkMojibake(text) !== null) return true;
169
- let hits = 0;
170
- for (const ch of GBK_MOJIBAKE_FINGERPRINTS) {
171
- if (text.includes(ch) && ++hits >= 2) return true;
172
- }
173
- return false;
174
- }
175
- function killProcessTree(proc) {
176
- if (proc.killed || proc.exitCode !== null) return;
177
- if (IS_WINDOWS && proc.pid) {
178
- try {
179
- const tk = spawn("taskkill", ["/PID", String(proc.pid), "/T", "/F"], {
180
- windowsHide: true,
181
- stdio: "ignore"
182
- });
183
- tk.on("exit", () => {
184
- try {
185
- proc.kill();
186
- } catch {
187
- }
188
- });
189
- tk.on("error", () => {
190
- try {
191
- proc.kill();
192
- } catch {
193
- }
194
- });
195
- } catch {
196
- try {
197
- proc.kill();
198
- } catch {
199
- }
200
- }
201
- return;
202
- }
203
- try {
204
- proc.kill("SIGTERM");
205
- } catch {
206
- }
207
- setTimeout(() => {
208
- if (!proc.killed && proc.exitCode === null) {
209
- try {
210
- proc.kill("SIGKILL");
211
- } catch {
212
- }
213
- }
214
- }, 500).unref();
215
- }
216
-
217
- // src/tools/shell-pool.ts
218
- var IS_WINDOWS2 = platform2() === "win32";
85
+ import { platform } from "os";
86
+ var IS_WINDOWS2 = platform() === "win32";
219
87
  var SHELL = IS_WINDOWS2 ? "powershell.exe" : process.env["SHELL"] ?? "/bin/bash";
220
88
  var pool = /* @__PURE__ */ new Map();
221
89
  var IDLE_EVICT_MS = 5 * 6e4;
222
90
  var evictionTimer = null;
223
91
  function spawnShell(cwd) {
224
92
  const shellArgs = IS_WINDOWS2 ? ["-NoProfile", "-NonInteractive", "-Command", "-"] : ["-s"];
225
- const proc = spawn2(SHELL, shellArgs, {
93
+ const proc = spawn(SHELL, shellArgs, {
226
94
  cwd,
227
95
  windowsHide: true,
228
96
  stdio: ["pipe", "pipe", "pipe"]
@@ -433,16 +301,22 @@ function isToolCapableProvider(provider) {
433
301
  }
434
302
 
435
303
  // src/tools/builtin/bash.ts
436
- import { spawn as spawn3 } from "child_process";
304
+ import { spawn as spawn2 } from "child_process";
437
305
  import { existsSync as existsSync2, readdirSync, statSync } from "fs";
438
- import { platform as platform3 } from "os";
306
+ import { platform as platform2 } from "os";
439
307
  import { resolve } from "path";
440
308
 
441
309
  // src/tools/undo-stack.ts
442
- import { readFileSync, writeFileSync, unlinkSync, rmdirSync, existsSync } from "fs";
310
+ import { writeFileSync, unlinkSync, rmdirSync, existsSync } from "fs";
443
311
  var MAX_UNDO_DEPTH = 20;
444
312
  var UndoStack = class {
445
- stack = [];
313
+ stateKey = /* @__PURE__ */ Symbol("stack");
314
+ get stack() {
315
+ return scopedState(this.stateKey, () => ({ items: [] })).items;
316
+ }
317
+ set stack(items) {
318
+ scopedState(this.stateKey, () => ({ items: [] })).items = items;
319
+ }
446
320
  /**
447
321
  * 在执行文件写入操作之前调用,保存当前状态。
448
322
  * @param filePath 将要被写入的文件路径
@@ -451,11 +325,7 @@ var UndoStack = class {
451
325
  push(filePath, description) {
452
326
  let previousContent = null;
453
327
  if (existsSync(filePath)) {
454
- try {
455
- previousContent = readFileSync(filePath, "utf-8");
456
- } catch {
457
- return;
458
- }
328
+ previousContent = readSnapshot(filePath);
459
329
  }
460
330
  this.pushEntry({
461
331
  filePath,
@@ -492,7 +362,7 @@ var UndoStack = class {
492
362
  /** 内部统一入栈方法,含溢出裁剪 */
493
363
  pushEntry(entry) {
494
364
  this.stack.push(entry);
495
- if (this.stack.length > MAX_UNDO_DEPTH) {
365
+ while (this.stack.length > MAX_UNDO_DEPTH || this.stack.reduce((n, e) => n + Buffer.byteLength(e.previousContent ?? ""), 0) > MAX_SNAPSHOT_TOTAL_BYTES) {
496
366
  this.stack.shift();
497
367
  }
498
368
  }
@@ -553,42 +423,8 @@ var UndoStack = class {
553
423
  };
554
424
  var undoStack = new UndoStack();
555
425
 
556
- // src/core/interrupt.ts
557
- var controller = new AbortController();
558
- var interrupted = false;
559
- function currentAbortSignal() {
560
- return controller.signal;
561
- }
562
- function isInterrupted() {
563
- return interrupted;
564
- }
565
- function requestInterrupt() {
566
- interrupted = true;
567
- if (!controller.signal.aborted) {
568
- controller.abort(new DOMException("User interrupted (Ctrl+C)", "AbortError"));
569
- }
570
- }
571
- function resetInterrupt() {
572
- controller = new AbortController();
573
- interrupted = false;
574
- }
575
-
576
- // src/tools/session-context.ts
577
- import { AsyncLocalStorage } from "async_hooks";
578
- var als = new AsyncLocalStorage();
579
- var DEFAULT_SESSION_KEY = "__default__";
580
- function getCurrentSessionKey() {
581
- const ctx = als.getStore();
582
- if (!ctx || !ctx.sessionKey) return DEFAULT_SESSION_KEY;
583
- return ctx.sessionKey;
584
- }
585
- function runWithSessionKey(sessionKey, fn) {
586
- const key = sessionKey && sessionKey.length > 0 ? sessionKey : DEFAULT_SESSION_KEY;
587
- return als.run({ sessionKey: key }, fn);
588
- }
589
-
590
426
  // src/tools/builtin/bash.ts
591
- var IS_WINDOWS3 = platform3() === "win32";
427
+ var IS_WINDOWS3 = platform2() === "win32";
592
428
  var SHELL2 = IS_WINDOWS3 ? "powershell.exe" : process.env["SHELL"] ?? "/bin/bash";
593
429
  var cwdBySession = /* @__PURE__ */ new Map();
594
430
  function getCwd() {
@@ -1171,7 +1007,7 @@ function pushBashUndoEntries(beforeSnapshot, parsedTargetsBefore, cwd) {
1171
1007
  function runShell(command, opts) {
1172
1008
  return new Promise((resolvePromise) => {
1173
1009
  const shellArgs = IS_WINDOWS3 ? ["-NoProfile", "-NonInteractive", "-Command", WIN_UTF8_PREAMBLE + command] : ["-c", command];
1174
- const child = spawn3(SHELL2, shellArgs, {
1010
+ const child = spawn2(SHELL2, shellArgs, {
1175
1011
  cwd: opts.cwd,
1176
1012
  env: opts.env,
1177
1013
  windowsHide: true,
@@ -1250,16 +1086,16 @@ function updateCwdFromCommand(command, baseCwd) {
1250
1086
  }
1251
1087
 
1252
1088
  // src/tools/builtin/read-file.ts
1253
- import { readFileSync as readFileSync2, existsSync as existsSync3, statSync as statSync2, readdirSync as readdirSync2 } from "fs";
1089
+ import { readFileSync, existsSync as existsSync3, statSync as statSync2, readdirSync as readdirSync2 } from "fs";
1254
1090
  import { execFileSync } from "child_process";
1255
1091
  import { extname, resolve as resolve2, basename, sep, dirname } from "path";
1256
1092
  import { homedir } from "os";
1257
1093
 
1258
1094
  // src/tools/builtin/notebook-utils.ts
1259
- function cellSourceToString(source) {
1260
- if (!source) return "";
1261
- if (Array.isArray(source)) return source.join("");
1262
- return source;
1095
+ function cellSourceToString(source2) {
1096
+ if (!source2) return "";
1097
+ if (Array.isArray(source2)) return source2.join("");
1098
+ return source2;
1263
1099
  }
1264
1100
  function stringToCellSource(content) {
1265
1101
  if (content.length === 0) return [];
@@ -1314,16 +1150,16 @@ function renderNotebookAsText(nb, filePath) {
1314
1150
  nb.cells.forEach((cell, idx) => {
1315
1151
  const cellNum = idx + 1;
1316
1152
  const type = cell.cell_type;
1317
- const source = cellSourceToString(cell.source);
1153
+ const source2 = cellSourceToString(cell.source);
1318
1154
  if (type === "markdown") {
1319
1155
  lines.push(`## Cell ${cellNum} [markdown]`);
1320
- lines.push(source);
1156
+ lines.push(source2);
1321
1157
  lines.push("");
1322
1158
  } else if (type === "code") {
1323
1159
  const execLabel = cell.execution_count != null ? ` (execution #${cell.execution_count})` : "";
1324
1160
  lines.push(`## Cell ${cellNum} [code${execLabel}]`);
1325
1161
  lines.push("```" + language);
1326
- lines.push(source);
1162
+ lines.push(source2);
1327
1163
  lines.push("```");
1328
1164
  const outputText = extractOutputText(cell.outputs);
1329
1165
  if (outputText) {
@@ -1335,7 +1171,7 @@ function renderNotebookAsText(nb, filePath) {
1335
1171
  lines.push("");
1336
1172
  } else if (type === "raw") {
1337
1173
  lines.push(`## Cell ${cellNum} [raw]`);
1338
- lines.push(source);
1174
+ lines.push(source2);
1339
1175
  lines.push("");
1340
1176
  }
1341
1177
  });
@@ -1360,6 +1196,7 @@ function createCell(cellType, content) {
1360
1196
  // src/tools/builtin/read-file.ts
1361
1197
  var MAX_FILE_BYTES = 10 * 1024 * 1024;
1362
1198
  function getHardRefusal(normalizedPath) {
1199
+ if (isCredentialVault(normalizedPath)) return "[Refused] Credential vault files cannot be read by tools.";
1363
1200
  const home2 = homedir();
1364
1201
  const p = normalizedPath.toLowerCase();
1365
1202
  const base = basename(normalizedPath).toLowerCase();
@@ -1534,7 +1371,7 @@ var readFileTool = {
1534
1371
  const offsetLine = typeof rawOffset === "number" && Number.isFinite(rawOffset) && rawOffset > 0 ? Math.floor(rawOffset) : void 0;
1535
1372
  const limitLines = typeof rawLimit === "number" && Number.isFinite(rawLimit) && rawLimit > 0 ? Math.floor(rawLimit) : void 0;
1536
1373
  if (!filePath) throw new ToolError("read_file", "path is required");
1537
- const normalizedPath = resolve2(filePath);
1374
+ const normalizedPath = canonicalPath(filePath);
1538
1375
  const refusal = getHardRefusal(normalizedPath);
1539
1376
  if (refusal) return refusal;
1540
1377
  if (!existsSync3(normalizedPath)) {
@@ -1601,7 +1438,7 @@ Suggestion: read existing text versions (.md / .txt) in the project, or install
1601
1438
  }
1602
1439
  if (ext === ".ipynb") {
1603
1440
  try {
1604
- const raw = readFileSync2(normalizedPath, "utf-8");
1441
+ const raw = readFileSync(normalizedPath, "utf-8");
1605
1442
  const nb = parseNotebook(raw);
1606
1443
  return renderNotebookAsText(nb, filePath);
1607
1444
  } catch (err) {
@@ -1619,8 +1456,8 @@ Do NOT claim you inspected this image \u2014 you have not seen it.`;
1619
1456
  return `[Binary file: ${filePath} (${ext})]
1620
1457
  This is a binary file and cannot be read as text. If there is a text version (.md / .txt) in the project, please read that instead.`;
1621
1458
  }
1622
- const { readFile: readFile2 } = await import("fs/promises");
1623
- const buf = size > 1048576 ? await readFile2(normalizedPath) : readFileSync2(normalizedPath);
1459
+ const { readFile } = await import("fs/promises");
1460
+ const buf = size > 1048576 ? await readFile(normalizedPath) : readFileSync(normalizedPath);
1624
1461
  if (encoding === "base64") {
1625
1462
  return `[File: ${filePath} | base64]
1626
1463
 
@@ -1658,12 +1495,12 @@ ${content}`;
1658
1495
  };
1659
1496
 
1660
1497
  // src/tools/builtin/write-file.ts
1661
- import { appendFileSync, mkdirSync, readFileSync as readFileSync5, statSync as statSync3 } from "fs";
1498
+ import { appendFileSync, mkdirSync, readFileSync as readFileSync3, statSync as statSync3 } from "fs";
1662
1499
  import { dirname as dirname3 } from "path";
1663
1500
 
1664
1501
  // src/tools/executor.ts
1665
1502
  import chalk2 from "chalk";
1666
- import { existsSync as existsSync6, readFileSync as readFileSync4 } from "fs";
1503
+ import { existsSync as existsSync5, readFileSync as readFileSync2 } from "fs";
1667
1504
  import { tmpdir } from "os";
1668
1505
 
1669
1506
  // src/core/readline-internal.ts
@@ -2006,7 +1843,8 @@ var READ_ONLY_TOOLS = /* @__PURE__ */ new Set([
2006
1843
  "recall_memory"
2007
1844
  ]);
2008
1845
  function normalizePathForPermission(value) {
2009
- return value.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
1846
+ const normalized = value.replace(/\\/g, "/").replace(/\/+$/, "");
1847
+ return process.platform === "win32" ? normalized.toLowerCase() : normalized;
2010
1848
  }
2011
1849
  function canonicalizeWithNearestParent(value) {
2012
1850
  const missing = [];
@@ -2094,7 +1932,7 @@ function profileHardDecision(profileName, toolName, args, dangerLevel, ctx) {
2094
1932
  return { action: "deny", reason: "file write target is outside workspace/temp roots", profileName, hardBoundary: true };
2095
1933
  }
2096
1934
  }
2097
- if (toolName === "bash" || toolName === "run_interactive" || toolName === "task_create") {
1935
+ if (toolName === "bash" || toolName === "run_interactive" || toolName === "task_create" || toolName === "run_tests") {
2098
1936
  return {
2099
1937
  action: "confirm",
2100
1938
  reason: "shell/process writes cannot be proven to stay inside workspace/temp roots",
@@ -2196,9 +2034,9 @@ function checkNetworkPolicy(toolName, args, policy) {
2196
2034
  if (!policy?.enabled) return null;
2197
2035
  let action = networkToolAction(toolName, policy);
2198
2036
  let reason = "network access requires confirmation";
2199
- if (toolName === "bash") {
2037
+ if (["bash", "run_tests", "run_interactive", "task_create"].includes(toolName)) {
2200
2038
  const command = String(args["command"] ?? "");
2201
- if (!isShellNetworkCommand(command)) return null;
2039
+ if (toolName === "bash" && !isShellNetworkCommand(command) && dangerIsReadOnly(command)) return null;
2202
2040
  action = policy.tools?.shell ?? policy.defaultAction ?? "confirm";
2203
2041
  reason = "shell command may access the network";
2204
2042
  }
@@ -2234,6 +2072,9 @@ function checkNetworkPolicy(toolName, args, policy) {
2234
2072
  if (!permissionAction) return null;
2235
2073
  return { action: permissionAction, reason, profileName: "networkPolicy" };
2236
2074
  }
2075
+ function dangerIsReadOnly(command) {
2076
+ return /^(?:pwd|echo [\w .-]+|git status(?: --short)?)$/.test(command.trim());
2077
+ }
2237
2078
 
2238
2079
  // src/tools/truncate.ts
2239
2080
  var DEFAULT_MAX_TOOL_OUTPUT_CHARS = 12e3;
@@ -2343,8 +2184,7 @@ function truncateOutput(content, toolName, maxChars) {
2343
2184
  }
2344
2185
 
2345
2186
  // src/tools/action-classifier.ts
2346
- import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
2347
- import { isAbsolute as isAbsolute2, join, resolve as resolve4 } from "path";
2187
+ import { isAbsolute as isAbsolute2, resolve as resolve4 } from "path";
2348
2188
  var RECENT_DENIED_LIMIT = 50;
2349
2189
  var recentlyDenied = [];
2350
2190
  function recordRecentlyDeniedAutoAction(call, classification) {
@@ -2364,7 +2204,8 @@ function clearRecentlyDeniedAutoActions() {
2364
2204
  recentlyDenied.splice(0, recentlyDenied.length);
2365
2205
  }
2366
2206
  function normalizePath(value) {
2367
- return value.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
2207
+ const normalized = canonicalPath(value).replace(/\\/g, "/").replace(/\/+$/, "");
2208
+ return process.platform === "win32" ? normalized.toLowerCase() : normalized;
2368
2209
  }
2369
2210
  function isInside(root, target) {
2370
2211
  const absoluteRoot = resolve4(root);
@@ -2377,56 +2218,6 @@ function getPathArg(call) {
2377
2218
  const raw = call.arguments.path ?? call.arguments.filePath ?? call.arguments.outputPath ?? call.arguments.file;
2378
2219
  return typeof raw === "string" && raw.trim() ? raw.trim() : null;
2379
2220
  }
2380
- function splitShellWords(command) {
2381
- const out = [];
2382
- const re = /"([^"]*)"|'([^']*)'|([^\s]+)/g;
2383
- let m;
2384
- while (m = re.exec(command)) out.push(m[1] ?? m[2] ?? m[3] ?? "");
2385
- return out.filter(Boolean);
2386
- }
2387
- function readPackageManifest(root) {
2388
- const p = join(root, "package.json");
2389
- if (!existsSync5(p)) return null;
2390
- try {
2391
- return JSON.parse(readFileSync3(p, "utf-8"));
2392
- } catch {
2393
- return null;
2394
- }
2395
- }
2396
- function manifestHasDependency(root, name) {
2397
- const manifest = readPackageManifest(root);
2398
- if (!manifest) return false;
2399
- for (const key of ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]) {
2400
- const deps = manifest[key];
2401
- if (deps && typeof deps === "object" && Object.prototype.hasOwnProperty.call(deps, name)) return true;
2402
- }
2403
- return false;
2404
- }
2405
- function lockfileMentions(root, name) {
2406
- for (const file of ["package-lock.json", "pnpm-lock.yaml", "yarn.lock"]) {
2407
- const p = join(root, file);
2408
- if (!existsSync5(p)) continue;
2409
- try {
2410
- if (readFileSync3(p, "utf-8").includes(name)) return true;
2411
- } catch {
2412
- }
2413
- }
2414
- return false;
2415
- }
2416
- function getInstallPackages(command) {
2417
- const words = splitShellWords(command);
2418
- const cmd = words[0]?.toLowerCase();
2419
- const sub = words[1]?.toLowerCase();
2420
- if (!cmd || !sub) return [];
2421
- const isInstall = cmd === "npm" && (sub === "install" || sub === "i" || sub === "add") || (cmd === "pnpm" || cmd === "yarn") && (sub === "add" || sub === "install");
2422
- if (!isInstall) return [];
2423
- return words.slice(2).filter((w) => !w.startsWith("-")).filter((w) => w !== "." && w !== "./").map((w) => w.replace(/^@([^/]+)\/([^@]+)(?:@.+)?$/, "@$1/$2").replace(/^([^@]+)@.+$/, "$1"));
2424
- }
2425
- function isPlainGitPush(command) {
2426
- if (!/^\s*git\s+push\b/i.test(command)) return false;
2427
- if (/--force|-f\b|\+[^\s]+/i.test(command)) return false;
2428
- return !/[;&|`$<>\n\r]/.test(command);
2429
- }
2430
2221
  function hasSecretExfiltrationShape(command) {
2431
2222
  return /(?:\.env|config\.json|id_rsa|id_ed25519|AWS_SECRET|AICLI_API_KEY|OPENAI_API_KEY|ANTHROPIC_API_KEY)/i.test(command) && /\b(curl|wget|scp|rsync|nc|netcat|Invoke-WebRequest|Invoke-RestMethod)\b/i.test(command);
2432
2223
  }
@@ -2448,7 +2239,7 @@ var RuleBasedActionClassifier = class {
2448
2239
  }
2449
2240
  return { decision: "confirm", reason: "destructive actions require confirmation", ruleId: "auto.destructive-confirm", confidence: "high" };
2450
2241
  }
2451
- classifyShell(call, dangerLevel, ctx) {
2242
+ classifyShell(call, dangerLevel, _ctx) {
2452
2243
  const command = String(call.arguments.command ?? "");
2453
2244
  if (/\bcurl\b[^\n|;&]*\|\s*(?:sudo\s+)?(?:bash|sh|zsh)\b/i.test(command)) {
2454
2245
  return { decision: "deny", reason: "curl pipe shell execution is blocked in auto mode", ruleId: "deny.curl-pipe-shell", confidence: "high" };
@@ -2474,18 +2265,7 @@ var RuleBasedActionClassifier = class {
2474
2265
  if (/\b(npx|pnpm\s+dlx|yarn\s+dlx)\s+(claude|codex|aider|cursor-agent|gemini)\b/i.test(command)) {
2475
2266
  return { decision: "confirm", reason: "third-party agent loop requires confirmation", ruleId: "confirm.third-party-agent", confidence: "high" };
2476
2267
  }
2477
- if (isPlainGitPush(command)) {
2478
- return { decision: "auto-approve", reason: "plain git push without force", ruleId: "auto.git-push", confidence: "high" };
2479
- }
2480
- const pkgs = getInstallPackages(command);
2481
- if (pkgs.length > 0) {
2482
- const allDeclared = pkgs.every((p) => manifestHasDependency(ctx.workspaceRoot, p) || lockfileMentions(ctx.workspaceRoot, p));
2483
- return allDeclared ? { decision: "auto-approve", reason: "installing dependencies already declared in manifest/lockfile", ruleId: "auto.declared-dependency-install", confidence: "medium" } : { decision: "confirm", reason: "dependency install includes undeclared packages", ruleId: "confirm.undeclared-dependency-install", confidence: "medium" };
2484
- }
2485
- if (/\b(curl|wget|Invoke-WebRequest|Invoke-RestMethod)\b/i.test(command) && !/[>|;&`$]/.test(command)) {
2486
- return { decision: "auto-approve", reason: "read-only HTTP shell command", ruleId: "auto.readonly-http-shell", confidence: "medium" };
2487
- }
2488
- if (dangerLevel === "safe") {
2268
+ if (dangerLevel === "safe" && getDangerLevel(call.name, call.arguments) === "safe") {
2489
2269
  return { decision: "auto-approve", reason: "safe shell action", ruleId: "auto.safe-shell", confidence: "medium" };
2490
2270
  }
2491
2271
  return { decision: "confirm", reason: "shell action is not proven low-risk", ruleId: "confirm.shell-default", confidence: "medium" };
@@ -2954,10 +2734,10 @@ var ToolExecutor = class {
2954
2734
  if (call.name === "write_file") {
2955
2735
  const { path: filePath, content: chunk, append } = resolveWriteFileArgs(call.arguments);
2956
2736
  if (!filePath) return;
2957
- if (existsSync6(filePath)) {
2737
+ if (existsSync5(filePath)) {
2958
2738
  let oldContent;
2959
2739
  try {
2960
- oldContent = readFileSync4(filePath, "utf-8");
2740
+ oldContent = readFileSync2(filePath, "utf-8");
2961
2741
  } catch {
2962
2742
  return;
2963
2743
  }
@@ -2981,7 +2761,7 @@ var ToolExecutor = class {
2981
2761
  }
2982
2762
  } else if (call.name === "edit_file") {
2983
2763
  const filePath = String(call.arguments["path"] ?? "");
2984
- if (!filePath || !existsSync6(filePath)) return;
2764
+ if (!filePath || !existsSync5(filePath)) return;
2985
2765
  const oldStr = call.arguments["old_str"];
2986
2766
  const newStr = call.arguments["new_str"];
2987
2767
  if (oldStr !== void 0) {
@@ -3008,7 +2788,7 @@ var ToolExecutor = class {
3008
2788
  const to = Number(call.arguments["delete_to_line"] ?? from);
3009
2789
  let fileContent;
3010
2790
  try {
3011
- fileContent = readFileSync4(filePath, "utf-8");
2791
+ fileContent = readFileSync2(filePath, "utf-8");
3012
2792
  } catch {
3013
2793
  return;
3014
2794
  }
@@ -3104,7 +2884,12 @@ import { resolve as resolve5, sep as sep2, basename as basename3 } from "path";
3104
2884
  import { homedir as homedir2 } from "os";
3105
2885
  var home = homedir2();
3106
2886
  function norm(p) {
3107
- const abs = resolve5(p);
2887
+ let abs;
2888
+ try {
2889
+ abs = canonicalPath(p);
2890
+ } catch {
2891
+ abs = resolve5(p);
2892
+ }
3108
2893
  return process.platform === "win32" ? abs.toLowerCase() : abs;
3109
2894
  }
3110
2895
  function homeRel(p) {
@@ -3118,6 +2903,7 @@ function homeRel(p) {
3118
2903
  }
3119
2904
  function classifyWritePath(path3) {
3120
2905
  if (!path3) return { sensitive: false };
2906
+ if (isCredentialVault(path3)) return { sensitive: true, reason: "credential vault" };
3121
2907
  const abs = norm(path3);
3122
2908
  const base = basename3(abs);
3123
2909
  const rel = homeRel(path3);
@@ -3200,7 +2986,7 @@ function countTextLines(text) {
3200
2986
 
3201
2987
  // src/tools/builtin/write-file.ts
3202
2988
  function countFileLines(filePath) {
3203
- return countTextLines(readFileSync5(filePath, "utf-8"));
2989
+ return countTextLines(readFileSync3(filePath, "utf-8"));
3204
2990
  }
3205
2991
  var writeFileTool = {
3206
2992
  definition: {
@@ -3264,7 +3050,7 @@ Do NOT split a long document into many write_file(append=true) calls. That patte
3264
3050
  const mode = appendMode ? "appended" : "written";
3265
3051
  void (async () => {
3266
3052
  try {
3267
- const { updateFile } = await import("./indexer-P3FPRTPJ.js");
3053
+ const { updateFile } = await import("./indexer-GKIWSSRP.js");
3268
3054
  await updateFile(process.cwd(), filePath);
3269
3055
  } catch {
3270
3056
  }
@@ -3283,7 +3069,7 @@ Do NOT split a long document into many write_file(append=true) calls. That patte
3283
3069
  };
3284
3070
 
3285
3071
  // src/tools/builtin/edit-file.ts
3286
- import { readFileSync as readFileSync6, existsSync as existsSync7 } from "fs";
3072
+ import { readFileSync as readFileSync4, existsSync as existsSync6 } from "fs";
3287
3073
 
3288
3074
  // src/tools/builtin/patch-apply.ts
3289
3075
  function parseUnifiedDiff(patch) {
@@ -3651,7 +3437,7 @@ Note: Path can be absolute or relative to cwd.`,
3651
3437
  const filePath = String(args["path"] ?? args["file"] ?? "");
3652
3438
  const encoding = args["encoding"] ?? "utf-8";
3653
3439
  if (!filePath) throw new ToolError("edit_file", "path is required");
3654
- if (!existsSync7(filePath)) throw new ToolError("edit_file", `File not found: ${filePath}`);
3440
+ if (!existsSync6(filePath)) throw new ToolError("edit_file", `File not found: ${filePath}`);
3655
3441
  const verdict = classifyWritePath(filePath);
3656
3442
  if (verdict.sensitive && subAgentGuard.active) {
3657
3443
  throw new ToolError(
@@ -3659,7 +3445,7 @@ Note: Path can be absolute or relative to cwd.`,
3659
3445
  `Refused: sub-agents cannot edit sensitive paths \u2014 ${verdict.reason}. If this is genuinely needed, ask the parent agent to perform the edit so the user can approve it.`
3660
3446
  );
3661
3447
  }
3662
- const original = readFileSync6(filePath, encoding);
3448
+ const original = readFileSync4(filePath, encoding);
3663
3449
  if (args["patch"] !== void 0) {
3664
3450
  const patchText = String(args["patch"] ?? "");
3665
3451
  const stopOnError = args["stop_on_error"] !== false;
@@ -3836,8 +3622,8 @@ function truncatePreview(str, maxLen = 80) {
3836
3622
  }
3837
3623
 
3838
3624
  // src/tools/builtin/list-dir.ts
3839
- import { readdirSync as readdirSync3, statSync as statSync4, existsSync as existsSync8 } from "fs";
3840
- import { join as join2, basename as basename4 } from "path";
3625
+ import { readdirSync as readdirSync3, statSync as statSync4, existsSync as existsSync7 } from "fs";
3626
+ import { join, basename as basename4 } from "path";
3841
3627
  var listDirTool = {
3842
3628
  definition: {
3843
3629
  name: "list_dir",
@@ -3859,7 +3645,7 @@ var listDirTool = {
3859
3645
  async execute(args) {
3860
3646
  const dirPath = String(args["path"] ?? process.cwd());
3861
3647
  const recursive = Boolean(args["recursive"] ?? false);
3862
- if (!existsSync8(dirPath)) {
3648
+ if (!existsSync7(dirPath)) {
3863
3649
  const targetName = basename4(dirPath).toLowerCase();
3864
3650
  const cwd = process.cwd();
3865
3651
  const suggestions = [];
@@ -3917,11 +3703,11 @@ function listRecursive(basePath, indent, recursive, lines) {
3917
3703
  if (entry.isDirectory()) {
3918
3704
  lines.push(`${indent}\u{1F4C1} ${entry.name}/`);
3919
3705
  if (recursive) {
3920
- listRecursive(join2(basePath, entry.name), indent + " ", true, lines);
3706
+ listRecursive(join(basePath, entry.name), indent + " ", true, lines);
3921
3707
  }
3922
3708
  } else {
3923
3709
  try {
3924
- const stat = statSync4(join2(basePath, entry.name));
3710
+ const stat = statSync4(join(basePath, entry.name));
3925
3711
  const size = formatSize(stat.size);
3926
3712
  lines.push(`${indent}\u{1F4C4} ${entry.name} (${size})`);
3927
3713
  } catch {
@@ -3936,10 +3722,77 @@ function formatSize(bytes) {
3936
3722
  return `${(bytes / 1024 / 1024).toFixed(1)}MB`;
3937
3723
  }
3938
3724
 
3725
+ // src/tools/regex-search.ts
3726
+ import { Worker } from "worker_threads";
3727
+ var source = String.raw`
3728
+ const { parentPort, workerData: d } = require('node:worker_threads');
3729
+ const fs = require('node:fs');
3730
+ let re;
3731
+ try { re = new RegExp(d.pattern, d.ignoreCase ? 'i' : ''); }
3732
+ catch { re = new RegExp(d.pattern.replace(/[.*+?^$()|[\]\\]/g, '\\$&'), d.ignoreCase ? 'i' : ''); }
3733
+ const results = [];
3734
+ for (const f of d.files) {
3735
+ if (results.length >= d.maxResults) break;
3736
+ let fd;
3737
+ try {
3738
+ fd = fs.openSync(f.fullPath, 'r');
3739
+ const buf = Buffer.alloc(1000001);
3740
+ let size = 0;
3741
+ while (size < buf.length) { const n = fs.readSync(fd, buf, size, buf.length - size, null); if (!n) break; size += n; }
3742
+ if (size > 1000000 || buf.subarray(0, Math.min(size,512)).includes(0)) continue;
3743
+ const lines = buf.subarray(0,size).toString('utf8').split('\n');
3744
+ for (let i=0; i<lines.length && results.length<d.maxResults; i++) {
3745
+ if (!re.test(lines[i])) continue;
3746
+ const match = { file:f.relPath.replace(/\\/g,'/'), lineNumber:i+1, lineText:lines[i].slice(0,10000) };
3747
+ if (d.context) {
3748
+ match.contextBefore=[]; match.contextAfter=[];
3749
+ for(let j=Math.max(0,i-d.context);j<i;j++) match.contextBefore.push([j+1,lines[j].slice(0,10000)]);
3750
+ for(let j=i+1;j<=Math.min(lines.length-1,i+d.context);j++) match.contextAfter.push([j+1,lines[j].slice(0,10000)]);
3751
+ }
3752
+ results.push(match);
3753
+ }
3754
+ } catch {} finally { if (fd !== undefined) fs.closeSync(fd); }
3755
+ }
3756
+ parentPort.postMessage(results);
3757
+ `;
3758
+ var active = 0;
3759
+ async function searchInWorker(files, pattern, ignoreCase, context, maxResults) {
3760
+ const signal = currentAbortSignal();
3761
+ if (signal.aborted) throw new Error("Search cancelled");
3762
+ if (active >= 4) throw new Error("Search capacity reached; wait for another search to finish");
3763
+ active++;
3764
+ let worker;
3765
+ try {
3766
+ return await new Promise((resolve7, reject) => {
3767
+ worker = new Worker(source, { eval: true, workerData: { files, pattern, ignoreCase, context, maxResults }, resourceLimits: { maxOldGenerationSizeMb: 64 } });
3768
+ let settled = false;
3769
+ const finish = (error, results) => {
3770
+ if (settled) return;
3771
+ settled = true;
3772
+ clearTimeout(timer);
3773
+ signal.removeEventListener("abort", abort);
3774
+ if (error) reject(error);
3775
+ else resolve7(results ?? []);
3776
+ };
3777
+ const timer = setTimeout(() => finish(new Error("Search exceeded 2 second CPU/time budget; simplify the pattern or narrow the path")), 2e3);
3778
+ const abort = () => finish(new Error("Search cancelled"));
3779
+ signal.addEventListener("abort", abort, { once: true });
3780
+ if (signal.aborted) abort();
3781
+ worker.once("message", (results) => finish(void 0, results));
3782
+ worker.once("error", (error) => finish(error));
3783
+ worker.once("exit", (code) => {
3784
+ if (!settled) finish(new Error(`Search worker exited (${code})`));
3785
+ });
3786
+ });
3787
+ } finally {
3788
+ if (worker) await worker.terminate();
3789
+ active--;
3790
+ }
3791
+ }
3792
+
3939
3793
  // src/tools/builtin/grep-files.ts
3940
- import { readdirSync as readdirSync4, readFileSync as readFileSync7, statSync as statSync5, existsSync as existsSync9 } from "fs";
3941
- import { readFile } from "fs/promises";
3942
- import { join as join3, relative } from "path";
3794
+ import { readdirSync as readdirSync4, statSync as statSync5, existsSync as existsSync8 } from "fs";
3795
+ import { join as join2, relative } from "path";
3943
3796
  var grepFilesTool = {
3944
3797
  definition: {
3945
3798
  name: "grep_files",
@@ -3987,43 +3840,24 @@ Supports regex. Automatically skips node_modules, dist, .git directories.`,
3987
3840
  const rootPath = String(args["path"] ?? process.cwd());
3988
3841
  const filePattern = args["file_pattern"] ? String(args["file_pattern"]) : void 0;
3989
3842
  const ignoreCase = Boolean(args["ignore_case"] ?? false);
3990
- const contextLines = Math.max(0, Number(args["context_lines"] ?? 0));
3991
- const maxResults = Math.max(1, Number(args["max_results"] ?? 50));
3843
+ const contextLines = Math.min(10, Math.max(0, Number(args["context_lines"]) || 0));
3844
+ const maxResults = Math.min(200, Math.max(1, Number(args["max_results"]) || 50));
3992
3845
  if (!pattern) throw new ToolError("grep_files", "pattern is required");
3993
- if (!existsSync9(rootPath)) throw new ToolError("grep_files", `Path not found: ${rootPath}`);
3846
+ if (!existsSync8(rootPath)) throw new ToolError("grep_files", `Path not found: ${rootPath}`);
3994
3847
  const MAX_PATTERN_LENGTH = 1e3;
3995
- if (pattern.length > MAX_PATTERN_LENGTH) {
3848
+ if (pattern.length > MAX_PATTERN_LENGTH || (filePattern?.length ?? 0) > MAX_PATTERN_LENGTH) {
3996
3849
  throw new ToolError("grep_files", `Pattern too long (${pattern.length} chars, max ${MAX_PATTERN_LENGTH}). Use a shorter pattern.`);
3997
3850
  }
3998
- let regex;
3999
- try {
4000
- regex = new RegExp(pattern, ignoreCase ? "gi" : "g");
4001
- } catch {
4002
- const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4003
- regex = new RegExp(escaped, ignoreCase ? "gi" : "g");
4004
- }
4005
- const results = [];
4006
- const stat = statSync5(rootPath);
4007
- if (stat.isFile()) {
4008
- searchInFile(rootPath, rootPath, regex, contextLines, maxResults, results);
4009
- } else {
4010
- const filePaths = collectFilePaths(rootPath, filePattern, maxResults);
4011
- const BATCH_SIZE = 16;
4012
- for (let i = 0; i < filePaths.length && results.length < maxResults; i += BATCH_SIZE) {
4013
- const batch = filePaths.slice(i, i + BATCH_SIZE);
4014
- const batchResults = await Promise.all(
4015
- batch.map(
4016
- ({ fullPath, relPath }) => searchInFileAsync(fullPath, relPath, regex, contextLines, maxResults - results.length)
4017
- )
4018
- );
4019
- for (const br of batchResults) {
4020
- for (const r of br) {
4021
- if (results.length >= maxResults) break;
4022
- results.push(r);
4023
- }
4024
- }
3851
+ assertReadablePath(rootPath);
3852
+ const paths = statSync5(rootPath).isFile() ? [{ fullPath: rootPath, relPath: rootPath }] : collectFilePaths(rootPath, filePattern, maxResults);
3853
+ const files = paths.filter((f) => {
3854
+ try {
3855
+ return !isCredentialVault(f.fullPath);
3856
+ } catch {
3857
+ return false;
4025
3858
  }
4026
- }
3859
+ });
3860
+ const results = await searchInWorker(files, pattern, ignoreCase, contextLines, maxResults);
4027
3861
  if (results.length === 0) {
4028
3862
  return `No matches found for pattern: ${pattern}
4029
3863
  Searched in: ${rootPath}${filePattern ? `
@@ -4063,8 +3897,23 @@ File filter: ${filePattern}` : ""}`;
4063
3897
  var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "dist-cjs", "release", ".next", "__pycache__", ".cache", "coverage", ".nyc_output"]);
4064
3898
  var BINARY_EXTS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg", ".ico", ".woff", ".woff2", ".ttf", ".eot", ".otf", ".mp3", ".mp4", ".wav", ".zip", ".tar", ".gz", ".rar", ".7z", ".exe", ".dll", ".so", ".dylib", ".pdf", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx"]);
4065
3899
  function matchesFilePattern(filename, pattern) {
4066
- const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
4067
- return new RegExp(`^${escaped}$`, "i").test(filename);
3900
+ filename = filename.toLowerCase();
3901
+ pattern = pattern.toLowerCase();
3902
+ let i = 0, j = 0, star = -1, restart = 0;
3903
+ while (i < filename.length) {
3904
+ if (pattern[j] === filename[i]) {
3905
+ i++;
3906
+ j++;
3907
+ } else if (pattern[j] === "*") {
3908
+ star = j++;
3909
+ restart = i;
3910
+ } else if (star >= 0) {
3911
+ j = star + 1;
3912
+ i = ++restart;
3913
+ } else return false;
3914
+ }
3915
+ while (pattern[j] === "*") j++;
3916
+ return j === pattern.length;
4068
3917
  }
4069
3918
  function isBinary(filename) {
4070
3919
  const ext = filename.slice(filename.lastIndexOf(".")).toLowerCase();
@@ -4072,8 +3921,9 @@ function isBinary(filename) {
4072
3921
  }
4073
3922
  function collectFilePaths(rootPath, filePattern, maxFiles) {
4074
3923
  const paths = [];
4075
- function walk(dirPath) {
4076
- if (paths.length >= maxFiles) return;
3924
+ let visited = 0;
3925
+ function walk(dirPath, depth = 0) {
3926
+ if (paths.length >= maxFiles || depth > 20 || visited >= 2e4) return;
4077
3927
  let entries;
4078
3928
  try {
4079
3929
  entries = readdirSync4(dirPath, { withFileTypes: true });
@@ -4081,14 +3931,14 @@ function collectFilePaths(rootPath, filePattern, maxFiles) {
4081
3931
  return;
4082
3932
  }
4083
3933
  for (const entry of entries) {
4084
- if (paths.length >= maxFiles) return;
3934
+ if (paths.length >= maxFiles || ++visited > 2e4) return;
4085
3935
  if (entry.isDirectory()) {
4086
3936
  if (SKIP_DIRS.has(entry.name) || entry.name.startsWith(".")) continue;
4087
- walk(join3(dirPath, entry.name));
3937
+ walk(join2(dirPath, entry.name), depth + 1);
4088
3938
  } else if (entry.isFile()) {
4089
3939
  if (isBinary(entry.name)) continue;
4090
3940
  if (filePattern && !matchesFilePattern(entry.name, filePattern)) continue;
4091
- const fullPath = join3(dirPath, entry.name);
3941
+ const fullPath = join2(dirPath, entry.name);
4092
3942
  try {
4093
3943
  if (statSync5(fullPath).size > 1e6) continue;
4094
3944
  } catch {
@@ -4101,81 +3951,10 @@ function collectFilePaths(rootPath, filePattern, maxFiles) {
4101
3951
  walk(rootPath);
4102
3952
  return paths;
4103
3953
  }
4104
- async function searchInFileAsync(fullPath, displayPath, regex, contextLines, maxResults) {
4105
- let content;
4106
- try {
4107
- content = await readFile(fullPath, "utf-8");
4108
- } catch {
4109
- return [];
4110
- }
4111
- const results = [];
4112
- const lines = content.split("\n");
4113
- regex.lastIndex = 0;
4114
- for (let i = 0; i < lines.length && results.length < maxResults; i++) {
4115
- regex.lastIndex = 0;
4116
- if (regex.test(lines[i])) {
4117
- const match = {
4118
- file: displayPath.replace(/\\/g, "/"),
4119
- lineNumber: i + 1,
4120
- lineText: lines[i]
4121
- };
4122
- if (contextLines > 0) {
4123
- match.contextBefore = [];
4124
- for (let j = Math.max(0, i - contextLines); j < i; j++) {
4125
- match.contextBefore.push([j + 1, lines[j]]);
4126
- }
4127
- match.contextAfter = [];
4128
- for (let j = i + 1; j <= Math.min(lines.length - 1, i + contextLines); j++) {
4129
- match.contextAfter.push([j + 1, lines[j]]);
4130
- }
4131
- }
4132
- results.push(match);
4133
- }
4134
- }
4135
- return results;
4136
- }
4137
- function searchInFile(fullPath, displayPath, regex, contextLines, maxResults, results) {
4138
- try {
4139
- const stat = statSync5(fullPath);
4140
- if (stat.size > 1e6) return;
4141
- } catch {
4142
- return;
4143
- }
4144
- let content;
4145
- try {
4146
- content = readFileSync7(fullPath, "utf-8");
4147
- } catch {
4148
- return;
4149
- }
4150
- const lines = content.split("\n");
4151
- regex.lastIndex = 0;
4152
- for (let i = 0; i < lines.length; i++) {
4153
- if (results.length >= maxResults) return;
4154
- regex.lastIndex = 0;
4155
- if (regex.test(lines[i])) {
4156
- const result = {
4157
- file: displayPath,
4158
- lineNumber: i + 1,
4159
- lineText: lines[i].trimEnd()
4160
- };
4161
- if (contextLines > 0) {
4162
- result.contextBefore = [];
4163
- result.contextAfter = [];
4164
- for (let c = Math.max(0, i - contextLines); c < i; c++) {
4165
- result.contextBefore.push([c + 1, lines[c].trimEnd()]);
4166
- }
4167
- for (let c = i + 1; c <= Math.min(lines.length - 1, i + contextLines); c++) {
4168
- result.contextAfter.push([c + 1, lines[c].trimEnd()]);
4169
- }
4170
- }
4171
- results.push(result);
4172
- }
4173
- }
4174
- }
4175
3954
 
4176
3955
  // src/tools/builtin/glob-files.ts
4177
- import { readdirSync as readdirSync5, statSync as statSync6, existsSync as existsSync10 } from "fs";
4178
- import { join as join4, relative as relative2, basename as basename5 } from "path";
3956
+ import { readdirSync as readdirSync5, statSync as statSync6, existsSync as existsSync9 } from "fs";
3957
+ import { join as join3, relative as relative2, basename as basename5 } from "path";
4179
3958
  var globFilesTool = {
4180
3959
  definition: {
4181
3960
  name: "glob_files",
@@ -4208,7 +3987,7 @@ Results sorted by most recent modification time. Automatically skips node_module
4208
3987
  const rootPath = String(args["path"] ?? process.cwd());
4209
3988
  const maxResults = Math.max(1, Number(args["max_results"] ?? 100));
4210
3989
  if (!pattern) throw new ToolError("glob_files", "pattern is required");
4211
- if (!existsSync10(rootPath)) throw new ToolError("glob_files", `Path not found: ${rootPath}`);
3990
+ if (!existsSync9(rootPath)) throw new ToolError("glob_files", `Path not found: ${rootPath}`);
4212
3991
  const regex = globToRegex(pattern);
4213
3992
  const matches = [];
4214
3993
  collectMatchingFiles(rootPath, rootPath, regex, matches, maxResults);
@@ -4285,7 +4064,7 @@ function collectMatchingFiles(dirPath, rootPath, regex, results, maxResults) {
4285
4064
  }
4286
4065
  for (const entry of entries) {
4287
4066
  if (results.length >= maxResults) break;
4288
- const fullPath = join4(dirPath, entry.name);
4067
+ const fullPath = join3(dirPath, entry.name);
4289
4068
  if (entry.isDirectory()) {
4290
4069
  if (SKIP_DIRS2.has(entry.name) || entry.name.startsWith(".")) continue;
4291
4070
  collectMatchingFiles(fullPath, rootPath, regex, results, maxResults);
@@ -4304,9 +4083,9 @@ function collectMatchingFiles(dirPath, rootPath, regex, results, maxResults) {
4304
4083
  }
4305
4084
 
4306
4085
  // src/tools/builtin/run-interactive.ts
4307
- import { spawn as spawn4 } from "child_process";
4308
- import { platform as platform4 } from "os";
4309
- var IS_WINDOWS4 = platform4() === "win32";
4086
+ import { spawn as spawn3 } from "child_process";
4087
+ import { platform as platform3 } from "os";
4088
+ var IS_WINDOWS4 = platform3() === "win32";
4310
4089
  var runInteractiveTool = {
4311
4090
  definition: {
4312
4091
  name: "run_interactive",
@@ -4368,7 +4147,7 @@ var runInteractiveTool = {
4368
4147
  };
4369
4148
  const prefixWarnings = [argsTypeWarning, stdinTypeWarning].filter(Boolean).join("");
4370
4149
  return new Promise((resolve7) => {
4371
- const child = spawn4(executable, cmdArgs.map(String), {
4150
+ const child = spawn3(executable, cmdArgs.map(String), {
4372
4151
  cwd: process.cwd(),
4373
4152
  env,
4374
4153
  stdio: ["pipe", "pipe", "pipe"]
@@ -4638,8 +4417,8 @@ var webFetchTool = {
4638
4417
  if (e.message.startsWith("Blocked:")) throw e;
4639
4418
  throw new NetworkError(`Invalid URL: "${url}"`);
4640
4419
  }
4641
- const controller2 = new AbortController();
4642
- const timeoutId = setTimeout(() => controller2.abort(), 2e4);
4420
+ const controller = new AbortController();
4421
+ const timeoutId = setTimeout(() => controller.abort(), 2e4);
4643
4422
  let rawHtml;
4644
4423
  let finalUrl;
4645
4424
  let contentType;
@@ -4660,7 +4439,7 @@ var webFetchTool = {
4660
4439
  }
4661
4440
  await resolveAndCheck(parsedHop.hostname);
4662
4441
  const r = await fetch(currentUrl, {
4663
- signal: controller2.signal,
4442
+ signal: controller.signal,
4664
4443
  headers: FETCH_HEADERS,
4665
4444
  redirect: "manual",
4666
4445
  // 手动控制重定向
@@ -5023,8 +4802,8 @@ function parseGoogleResults(html, maxResults) {
5023
4802
  return results;
5024
4803
  }
5025
4804
  async function fetchHtml(url) {
5026
- const controller2 = new AbortController();
5027
- const timer = setTimeout(() => controller2.abort(), REQUEST_TIMEOUT_MS);
4805
+ const controller = new AbortController();
4806
+ const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
5028
4807
  try {
5029
4808
  const res = await fetch(url, {
5030
4809
  method: "GET",
@@ -5033,7 +4812,7 @@ async function fetchHtml(url) {
5033
4812
  "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9",
5034
4813
  "Accept-Language": "en-US,en;q=0.9,zh-CN;q=0.8"
5035
4814
  },
5036
- signal: controller2.signal
4815
+ signal: controller.signal
5037
4816
  });
5038
4817
  if (!res.ok) {
5039
4818
  throw new NetworkError(`HTTP ${res.status} ${res.statusText}`, res.status);
@@ -5127,10 +4906,8 @@ Any of these triggers means use save_last_response, NOT write_file:
5127
4906
  };
5128
4907
 
5129
4908
  // src/tools/builtin/save-memory.ts
5130
- import { join as join5 } from "path";
5131
- import { homedir as homedir3 } from "os";
5132
4909
  function getConfigDir() {
5133
- return join5(homedir3(), CONFIG_DIR_NAME);
4910
+ return getExecutionConfigDir();
5134
4911
  }
5135
4912
  function parseSensitivity(value) {
5136
4913
  return value === "low" || value === "medium" || value === "high" ? value : void 0;
@@ -5394,15 +5171,15 @@ var googleSearchTool = {
5394
5171
  url.searchParams.set("cx", cx);
5395
5172
  url.searchParams.set("q", query);
5396
5173
  url.searchParams.set("num", String(numResults));
5397
- const controller2 = new AbortController();
5398
- const timeout = setTimeout(() => controller2.abort(), REQUEST_TIMEOUT_MS2);
5174
+ const controller = new AbortController();
5175
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS2);
5399
5176
  try {
5400
5177
  const response = await fetch(url.toString(), {
5401
5178
  method: "GET",
5402
5179
  headers: {
5403
5180
  "Accept": "application/json"
5404
5181
  },
5405
- signal: controller2.signal
5182
+ signal: controller.signal
5406
5183
  });
5407
5184
  if (!response.ok) {
5408
5185
  const errorBody = await response.text().catch(() => "");
@@ -5479,9 +5256,9 @@ function formatResults2(query, data, _requested) {
5479
5256
  }
5480
5257
 
5481
5258
  // src/agents/agent-config.ts
5482
- import { existsSync as existsSync11, readdirSync as readdirSync6, readFileSync as readFileSync8 } from "fs";
5483
- import { join as join6 } from "path";
5484
- import { homedir as homedir4 } from "os";
5259
+ import { existsSync as existsSync10, readdirSync as readdirSync6, readFileSync as readFileSync5 } from "fs";
5260
+ import { join as join4 } from "path";
5261
+ import { homedir as homedir3 } from "os";
5485
5262
  var READ_ONLY_TOOLS2 = /* @__PURE__ */ new Set([
5486
5263
  "read_file",
5487
5264
  "list_dir",
@@ -5552,13 +5329,13 @@ function normalizeName(name) {
5552
5329
  function isStringArray(value) {
5553
5330
  return Array.isArray(value) && value.every((item) => typeof item === "string");
5554
5331
  }
5555
- function parseAgentConfig(raw, source, path3) {
5332
+ function parseAgentConfig(raw, source2, path3) {
5556
5333
  if (!raw || typeof raw !== "object") return null;
5557
5334
  const obj = raw;
5558
5335
  if (typeof obj.name !== "string" || obj.name.trim() === "") return null;
5559
5336
  const cfg = {
5560
5337
  name: obj.name.trim(),
5561
- source,
5338
+ source: source2,
5562
5339
  path: path3
5563
5340
  };
5564
5341
  if (typeof obj.description === "string") cfg.description = obj.description;
@@ -5574,15 +5351,15 @@ function parseAgentConfig(raw, source, path3) {
5574
5351
  if (typeof obj.contextPolicy === "string") cfg.contextPolicy = obj.contextPolicy;
5575
5352
  return cfg;
5576
5353
  }
5577
- function loadAgentDir(dir, source) {
5578
- if (!existsSync11(dir)) return [];
5354
+ function loadAgentDir(dir, source2) {
5355
+ if (!existsSync10(dir)) return [];
5579
5356
  const out = [];
5580
5357
  for (const file of readdirSync6(dir)) {
5581
5358
  if (!file.endsWith(".json")) continue;
5582
- const path3 = join6(dir, file);
5359
+ const path3 = join4(dir, file);
5583
5360
  try {
5584
- const parsed = JSON.parse(readFileSync8(path3, "utf-8"));
5585
- const cfg = parseAgentConfig(parsed, source, path3);
5361
+ const parsed = JSON.parse(readFileSync5(path3, "utf-8"));
5362
+ const cfg = parseAgentConfig(parsed, source2, path3);
5586
5363
  if (cfg) out.push(cfg);
5587
5364
  } catch {
5588
5365
  }
@@ -5591,8 +5368,8 @@ function loadAgentDir(dir, source) {
5591
5368
  }
5592
5369
  function getAgentDirs(configDir, cwd = process.cwd()) {
5593
5370
  return {
5594
- user: join6(configDir ?? join6(homedir4(), CONFIG_DIR_NAME), "agents"),
5595
- project: join6(cwd, CONFIG_DIR_NAME, "agents")
5371
+ user: join4(configDir ?? join4(homedir3(), CONFIG_DIR_NAME), "agents"),
5372
+ project: join4(cwd, CONFIG_DIR_NAME, "agents")
5596
5373
  };
5597
5374
  }
5598
5375
  function listAgentConfigs(configDir, cwd = process.cwd()) {
@@ -6095,9 +5872,8 @@ var spawnAgentTool = {
6095
5872
  };
6096
5873
 
6097
5874
  // src/tools/builtin/task-manager.ts
6098
- import { spawn as spawn5 } from "child_process";
5875
+ import { spawn as spawn4 } from "child_process";
6099
5876
  import { randomUUID } from "crypto";
6100
- import { platform as platform5 } from "os";
6101
5877
  var MAX_OUTPUT_CHARS = 1e4;
6102
5878
  var MAX_OUTPUT_BYTES = 64 * 1024;
6103
5879
  var MAX_TASKS = 20;
@@ -6116,14 +5892,16 @@ function decodeField(chunks) {
6116
5892
  }
6117
5893
  function appendOutput(task, field, chunk) {
6118
5894
  const chunks = field === "stdout" ? task._stdoutChunks : task._stderrChunks;
6119
- chunks.push(chunk);
5895
+ chunks.push(chunk.length > MAX_OUTPUT_BYTES ? chunk.subarray(-MAX_OUTPUT_BYTES) : chunk);
6120
5896
  capChunks(chunks);
6121
5897
  task[field] = decodeField(chunks);
6122
5898
  }
6123
5899
  function createTask(command, description) {
5900
+ if ([...tasks.values()].filter((t) => t._active && t.owner === getExecutionConfigDir()).length >= 10) throw new Error("Background task limit reached (10 running per user).");
5901
+ if ([...tasks.values()].filter((t) => t._active).length >= MAX_TASKS) throw new Error("Background task limit reached (20 running). Stop a task before creating another.");
6124
5902
  if (tasks.size >= MAX_TASKS) {
6125
5903
  for (const [id2, t] of tasks) {
6126
- if (t.status !== "running") {
5904
+ if (!t._active) {
6127
5905
  tasks.delete(id2);
6128
5906
  break;
6129
5907
  }
@@ -6131,16 +5909,19 @@ function createTask(command, description) {
6131
5909
  }
6132
5910
  const id = randomUUID().slice(0, 8);
6133
5911
  const { shell, args } = shellInvocation(command);
6134
- const proc = spawn5(shell, args, {
5912
+ const proc = spawn4(shell, args, {
6135
5913
  stdio: ["ignore", "pipe", "pipe"],
6136
- detached: false,
5914
+ detached: process.platform !== "win32",
5915
+ windowsHide: true,
6137
5916
  env: { ...process.env, PYTHONUTF8: "1", PYTHONIOENCODING: "utf-8" }
6138
5917
  });
6139
5918
  const task = {
6140
5919
  id,
5920
+ owner: getExecutionConfigDir(),
6141
5921
  command,
6142
5922
  description,
6143
5923
  process: proc,
5924
+ _active: true,
6144
5925
  startTime: Date.now(),
6145
5926
  endTime: null,
6146
5927
  status: "running",
@@ -6153,11 +5934,13 @@ function createTask(command, description) {
6153
5934
  proc.stdout?.on("data", (chunk) => appendOutput(task, "stdout", chunk));
6154
5935
  proc.stderr?.on("data", (chunk) => appendOutput(task, "stderr", chunk));
6155
5936
  proc.on("close", (code) => {
5937
+ task._active = false;
6156
5938
  task.endTime = Date.now();
6157
5939
  task.exitCode = code;
6158
- task.status = code === 0 ? "completed" : "failed";
5940
+ if (task.status !== "stopped") task.status = code === 0 ? "completed" : "failed";
6159
5941
  });
6160
5942
  proc.on("error", (err) => {
5943
+ task._active = false;
6161
5944
  task.endTime = Date.now();
6162
5945
  task.status = "failed";
6163
5946
  task.stderr += `
@@ -6167,43 +5950,20 @@ Process error: ${err.message}`;
6167
5950
  return task;
6168
5951
  }
6169
5952
  function listTasks() {
6170
- return [...tasks.values()].map(
6171
- ({ process: _p, _stdoutChunks: _o, _stderrChunks: _e, ...rest }) => rest
5953
+ return [...tasks.values()].filter((t) => t.owner === getExecutionConfigDir()).map(
5954
+ ({ process: _p, _stdoutChunks: _o, _stderrChunks: _e, _active: _a, ...rest }) => rest
6172
5955
  );
6173
5956
  }
6174
5957
  function getTaskOutput(id) {
6175
5958
  const task = tasks.get(id);
6176
- if (!task) return null;
5959
+ if (!task || task.owner !== getExecutionConfigDir()) return null;
6177
5960
  return { stdout: task.stdout, stderr: task.stderr, status: task.status };
6178
5961
  }
6179
5962
  function stopTask(id) {
6180
5963
  const task = tasks.get(id);
6181
- if (!task || task.status !== "running") return false;
5964
+ if (!task || task.owner !== getExecutionConfigDir() || task.status !== "running") return false;
6182
5965
  try {
6183
- const proc = task.process;
6184
- if (platform5() === "win32" && proc.pid) {
6185
- try {
6186
- spawn5("taskkill", ["/PID", String(proc.pid), "/T", "/F"], {
6187
- windowsHide: true,
6188
- stdio: "ignore"
6189
- });
6190
- } catch {
6191
- }
6192
- try {
6193
- proc.kill();
6194
- } catch {
6195
- }
6196
- } else {
6197
- proc.kill("SIGTERM");
6198
- setTimeout(() => {
6199
- if (proc.exitCode === null && proc.signalCode === null) {
6200
- try {
6201
- proc.kill("SIGKILL");
6202
- } catch {
6203
- }
6204
- }
6205
- }, 3e3).unref();
6206
- }
5966
+ killProcessTree(task.process, true);
6207
5967
  task.status = "stopped";
6208
5968
  task.endTime = Date.now();
6209
5969
  return true;
@@ -6311,14 +6071,14 @@ var taskStopTool = {
6311
6071
 
6312
6072
  // src/tools/builtin/git-tools.ts
6313
6073
  import { execFileSync as execFileSync2 } from "child_process";
6314
- import { existsSync as existsSync12 } from "fs";
6315
- import { join as join7 } from "path";
6074
+ import { existsSync as existsSync11 } from "fs";
6075
+ import { join as join5 } from "path";
6316
6076
  function assertGitRepo(cwd) {
6317
6077
  let dir = cwd;
6318
6078
  const root = dir.split(/[\\/]/)[0] + (dir.includes("\\") ? "\\" : "/");
6319
6079
  while (dir && dir !== root) {
6320
- if (existsSync12(join7(dir, ".git"))) return;
6321
- const parent = join7(dir, "..");
6080
+ if (existsSync11(join5(dir, ".git"))) return;
6081
+ const parent = join5(dir, "..");
6322
6082
  if (parent === dir) break;
6323
6083
  dir = parent;
6324
6084
  }
@@ -6583,7 +6343,7 @@ ${commitOutput.trim()}`;
6583
6343
  };
6584
6344
 
6585
6345
  // src/tools/builtin/notebook-edit.ts
6586
- import { readFileSync as readFileSync9, existsSync as existsSync13 } from "fs";
6346
+ import { readFileSync as readFileSync6, existsSync as existsSync12 } from "fs";
6587
6347
  import { writeFile } from "fs/promises";
6588
6348
  import { resolve as resolve6, extname as extname2 } from "path";
6589
6349
  var notebookEditTool = {
@@ -6638,10 +6398,10 @@ var notebookEditTool = {
6638
6398
  if (extname2(absPath).toLowerCase() !== ".ipynb") {
6639
6399
  throw new ToolError("notebook_edit", "path must point to a .ipynb file");
6640
6400
  }
6641
- if (!existsSync13(absPath)) {
6401
+ if (!existsSync12(absPath)) {
6642
6402
  throw new ToolError("notebook_edit", `Notebook not found: ${filePath}`);
6643
6403
  }
6644
- const raw = readFileSync9(absPath, "utf-8");
6404
+ const raw = readFileSync6(absPath, "utf-8");
6645
6405
  const nb = parseNotebook(raw);
6646
6406
  const cellIdx0 = cellIndexRaw - 1;
6647
6407
  undoStack.push(absPath, `notebook_edit (${action}): ${filePath}`);
@@ -6967,8 +6727,6 @@ ${lines.join("\n")}`;
6967
6727
  };
6968
6728
 
6969
6729
  // src/tools/builtin/recall-memory.ts
6970
- import { join as join8 } from "path";
6971
- import { homedir as homedir5 } from "os";
6972
6730
  function formatHit(h, i) {
6973
6731
  const ts = h.chunk.timestamp.slice(0, 16).replace("T", " ");
6974
6732
  const title = h.chunk.sessionTitle ? ` \xB7 ${h.chunk.sessionTitle}` : "";
@@ -7018,7 +6776,7 @@ var recallMemoryTool = {
7018
6776
  const excludeCurrent = Boolean(args["excludeCurrentSession"]);
7019
6777
  const currentId = args["currentSessionId"] ? String(args["currentSessionId"]) : void 0;
7020
6778
  const minScore = args["minScore"] !== void 0 ? Number(args["minScore"]) : 0.25;
7021
- const configDir = join8(homedir5(), CONFIG_DIR_NAME);
6779
+ const configDir = getExecutionConfigDir();
7022
6780
  let persistentSection = "";
7023
6781
  try {
7024
6782
  const persistentHits = searchPersistentMemories(configDir, query, process.cwd(), 3);
@@ -7072,8 +6830,8 @@ function estimateToolDefinitionTokens(def) {
7072
6830
 
7073
6831
  // src/tools/registry.ts
7074
6832
  import { pathToFileURL } from "url";
7075
- import { existsSync as existsSync14, mkdirSync as mkdirSync3, readdirSync as readdirSync7 } from "fs";
7076
- import { join as join9 } from "path";
6833
+ import { existsSync as existsSync13, mkdirSync as mkdirSync3, readdirSync as readdirSync7 } from "fs";
6834
+ import { join as join6 } from "path";
7077
6835
  var ToolRegistry = class {
7078
6836
  tools = /* @__PURE__ */ new Map();
7079
6837
  pluginToolNames = /* @__PURE__ */ new Set();
@@ -7234,7 +6992,7 @@ var ToolRegistry = class {
7234
6992
  * Returns the number of successfully loaded plugins.
7235
6993
  */
7236
6994
  async loadPlugins(pluginsDir, allowPlugins = false) {
7237
- if (!existsSync14(pluginsDir)) {
6995
+ if (!existsSync13(pluginsDir)) {
7238
6996
  try {
7239
6997
  mkdirSync3(pluginsDir, { recursive: true });
7240
6998
  } catch {
@@ -7260,12 +7018,12 @@ var ToolRegistry = class {
7260
7018
  process.stderr.write(
7261
7019
  `
7262
7020
  [plugins] \u26A0 Loading ${files.length} plugin(s) with FULL system privileges:
7263
- ` + files.map((f) => ` + ${join9(pluginsDir, f)}`).join("\n") + "\n\n"
7021
+ ` + files.map((f) => ` + ${join6(pluginsDir, f)}`).join("\n") + "\n\n"
7264
7022
  );
7265
7023
  let loaded = 0;
7266
7024
  for (const file of files) {
7267
7025
  try {
7268
- const fileUrl = pathToFileURL(join9(pluginsDir, file)).href;
7026
+ const fileUrl = pathToFileURL(join6(pluginsDir, file)).href;
7269
7027
  const mod = await import(fileUrl);
7270
7028
  const tool = mod.tool ?? mod.default?.tool ?? mod.default;
7271
7029
  if (!tool || typeof tool.execute !== "function" || !tool.definition?.name) {
@@ -7306,10 +7064,6 @@ export {
7306
7064
  requestAgentStop,
7307
7065
  setPreferredAgentName,
7308
7066
  getPreferredAgentName,
7309
- isInterrupted,
7310
- requestInterrupt,
7311
- resetInterrupt,
7312
- runWithSessionKey,
7313
7067
  clearBashSession,
7314
7068
  rlInternal,
7315
7069
  groupCallsByPhase,