nolo-cli 0.10.0 → 0.12.0-alpha.1

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 (125) hide show
  1. package/{_executeModel-YAXYAUOK.js → _executeModel-MWAY74HA.js} +47 -44
  2. package/{action-TLI3V62L.js → action-RAXNQQKC.js} +9 -10
  3. package/{addContentAction-PLWJSCOZ.js → addContentAction-ZHI7TVV3.js} +8 -10
  4. package/{addDialogAgentAction-BJ6SB76X.js → addDialogAgentAction-JWXPKXNC.js} +7 -8
  5. package/{agentDeleteCommand-2VYX73U5.js → agentDeleteCommand-LXR5A5FV.js} +36 -26
  6. package/{agentEmailCommands-3GDZIX64.js → agentEmailCommands-DXRAP7BB.js} +63 -58
  7. package/{agentListCommands-UWVBT3LH.js → agentListCommands-ZFMS5ZLY.js} +132 -35
  8. package/{agentMachineCommands-WOKMUG2Y.js → agentMachineCommands-ZJLYVV52.js} +40 -29
  9. package/{agentPullCommand-O2MTUAFR.js → agentPullCommand-KRBRIFIY.js} +6 -6
  10. package/{agentRecordCommands-MVNZRH3U.js → agentRecordCommands-DFS626TB.js} +55 -50
  11. package/{agentRunCommand-K7THIJLA.js → agentRunCommand-GWBIE2TS.js} +60 -56
  12. package/{agentRunControl-K4VKNEHF.js → agentRunControl-IX4F5W2C.js} +3 -3
  13. package/{agentRuntimeLocal-WJIGU24U.js → agentRuntimeLocal-YNZSHMZC.js} +29 -14
  14. package/{agentSlice-LL5TKZEO.js → agentSlice-T2PXOEPQ.js} +9 -10
  15. package/{authCommand-TC2SW2CQ.js → authCommand-DMW336GT.js} +13 -13
  16. package/{authCommands-XPEHTKSM.js → authCommands-GH5T6RYV.js} +6 -6
  17. package/{chunk-5JGGZCZL.js → chunk-3HHTVM6G.js} +64 -5
  18. package/chunk-3QKO4EHV.js +112 -0
  19. package/{chunk-AUQ747NZ.js → chunk-43UD5ZP7.js} +76 -15
  20. package/{chunk-6SLCD4RB.js → chunk-4BTHUYMY.js} +596 -67
  21. package/{chunk-T7R3NGUU.js → chunk-4EH7AR52.js} +13 -13
  22. package/{chunk-C3RUNRHT.js → chunk-4GBNEA5B.js} +1 -1
  23. package/{chunk-4IJYA27C.js → chunk-4LG74DRG.js} +1 -1
  24. package/{chunk-2FTTDCD4.js → chunk-4ON5AV72.js} +2 -1
  25. package/{chunk-IWX4UW2E.js → chunk-524BDL33.js} +89 -8
  26. package/{chunk-EXXFGKF7.js → chunk-5ITRDSHY.js} +1400 -119
  27. package/{chunk-ISNCLGUZ.js → chunk-5NBHQD4D.js} +4 -3
  28. package/{chunk-7KNKFIRO.js → chunk-6LMFFG3K.js} +3 -3
  29. package/{chunk-PAKN26FP.js → chunk-6OVCRC72.js} +1 -1
  30. package/{chunk-CKOJQDKR.js → chunk-7JJCGV7N.js} +146 -3
  31. package/{chunk-KBKYAONQ.js → chunk-7MYHLZ3F.js} +2 -2
  32. package/{chunk-PIVFPOW6.js → chunk-AM53Q2SD.js} +175 -2541
  33. package/chunk-BYXBJQAS.js +0 -0
  34. package/{chunk-CL3IOKGK.js → chunk-DE5BA4O6.js} +20 -2
  35. package/{chunk-6KUOUWMP.js → chunk-DRMGJFGQ.js} +300 -89
  36. package/{chunk-QAALPJS7.js → chunk-EBH7G34M.js} +20 -7
  37. package/{chunk-HJGEVL7C.js → chunk-G3B356GF.js} +15 -17
  38. package/{chunk-OZMMKDBI.js → chunk-GDBCUWQ3.js} +26 -20
  39. package/{chunk-I5URP43I.js → chunk-GPZM7QH6.js} +9 -9
  40. package/chunk-IKXL2HZ3.js +70 -0
  41. package/{chunk-AS4PVWA4.js → chunk-IVAOVLM7.js} +1 -1
  42. package/{chunk-TNPIMII7.js → chunk-JUN3HFMN.js} +221 -81
  43. package/{chunk-HFASY3WO.js → chunk-JZDIW766.js} +1 -1
  44. package/{chunk-6JJWFVZ6.js → chunk-LHSJMY5V.js} +17 -17
  45. package/chunk-LTRXGFMS.js +83 -0
  46. package/{chunk-YIHFXH5Q.js → chunk-MEVCDATU.js} +3 -3
  47. package/{chunk-TILFE6LV.js → chunk-NCGFB7NN.js} +5 -3
  48. package/{chunk-BE2OOJ5P.js → chunk-NITP2JDG.js} +42 -11
  49. package/{chunk-YPRZSPF3.js → chunk-OCDEWRE2.js} +1 -1
  50. package/{chunk-VVX233PH.js → chunk-OHEKYYJF.js} +9 -9
  51. package/chunk-OOIN7HSO.js +146 -0
  52. package/{chunk-WPE4F4Z6.js → chunk-OPKKB25T.js} +1 -1
  53. package/{chunk-OR2XR25C.js → chunk-QCA3EPYK.js} +8 -8
  54. package/{chunk-SL35K7AH.js → chunk-QLUFR47U.js} +13 -83
  55. package/{chunk-CDTLMEB3.js → chunk-RNQ5BLR2.js} +1 -1
  56. package/{chunk-AVMKZHBC.js → chunk-RSEUHW6T.js} +3 -2
  57. package/{chunk-TYSCFY6S.js → chunk-RZ735TLX.js} +762 -167
  58. package/chunk-S3UTETIC.js +4885 -0
  59. package/chunk-S73OEOHH.js +2286 -0
  60. package/{chunk-JRKKFRAV.js → chunk-TSKWE74T.js} +58 -8
  61. package/{chunk-A67QFHAA.js → chunk-UG3WOFU6.js} +1 -1
  62. package/{chunk-4ZDM73SD.js → chunk-V2J635L5.js} +1 -1
  63. package/chunk-VC6OZXO5.js +298 -0
  64. package/{chunk-7U44LYTR.js → chunk-WPHL6ALY.js} +1 -1
  65. package/{chunk-4SIMLBEU.js → chunk-WTYKCU4W.js} +9 -9
  66. package/{chunk-NPVBXJY2.js → chunk-WWU275LU.js} +9 -9
  67. package/{chunk-ZQ2UM4KG.js → chunk-WZSRQNQG.js} +1 -292
  68. package/{chunk-DSZUWUYB.js → chunk-XHQWJUW4.js} +2 -2
  69. package/{chunk-GAKEBQD6.js → chunk-YDCZVUFP.js} +4 -0
  70. package/{chunk-2O4IZNTG.js → chunk-YTQLJYLB.js} +2 -2
  71. package/{chunk-3Y3HW5PX.js → chunk-ZPFRLGIZ.js} +1512 -935
  72. package/{chunk-5KLGBAAR.js → chunk-ZR4VHQ5D.js} +14 -13
  73. package/cliEnvHelpers-VYC2OPTB.js +30 -0
  74. package/{cliExecutor-DNE7TCC4.js → cliExecutor-K3F7DMRO.js} +2 -2
  75. package/{createAgentAutomationAction-CCE7CWGQ.js → createAgentAutomationAction-G32GKDGV.js} +22 -23
  76. package/{createDialogAction-QTRR4AT7.js → createDialogAction-TISANRZK.js} +25 -25
  77. package/{createPageAction-Y4KQOAJE.js → createPageAction-O7KOYNV4.js} +27 -27
  78. package/{dbSlice-WWNV2SGX.js → dbSlice-WDP2ZZHK.js} +7 -8
  79. package/{deleteFile-OW53A6BX.js → deleteFile-M7UNIUOO.js} +5 -5
  80. package/{dialogCommands-SMKIAC3Y.js → dialogCommands-6JPB3YQY.js} +33 -23
  81. package/{dialogSlice-PT7YBWNE.js → dialogSlice-PRHHYSP5.js} +21 -22
  82. package/{docCreateCommands-RFNA4HNM.js → docCreateCommands-75E6TZNN.js} +36 -25
  83. package/{docDeleteCommands-4YZ325M6.js → docDeleteCommands-EJHLN7LW.js} +37 -27
  84. package/{docReadCommands-RT6VAFFG.js → docReadCommands-RJYBC4FS.js} +36 -25
  85. package/{docStore-QUQZAFZF.js → docStore-H5VWKT5D.js} +22 -23
  86. package/{docUpdateCommands-EKKKNYPW.js → docUpdateCommands-MG4CNISV.js} +36 -25
  87. package/fetchMessages-KTNM7QAR.js +1 -1
  88. package/{getPricing-3E6UNPKL.js → getPricing-6TOZXPAU.js} +7 -3
  89. package/{handleSendMessageAction-5NSYAS53.js → handleSendMessageAction-QTUGICSZ.js} +22 -23
  90. package/index.js +77 -76
  91. package/{machineCommands-DOOSIUKG.js → machineCommands-MAV7M3TK.js} +67 -60
  92. package/{machineWsRunDispatch-PIOIQRBW.js → machineWsRunDispatch-ZOEGTKHC.js} +54 -49
  93. package/{memoryCommands-O4MIPIWQ.js → memoryCommands-AFMES3LN.js} +5 -5
  94. package/{models-4OPOOLFS.js → models-LSCRJJ6P.js} +2 -2
  95. package/package.json +1 -1
  96. package/{read-GW7ZEI23.js → read-2MIMRBZ2.js} +5 -5
  97. package/{readDocTool-HUFU6OCT.js → readDocTool-QHVGQHUJ.js} +1 -1
  98. package/{readlineWorkspace-QEAOH5EP.js → readlineWorkspace-SWQKC7ZS.js} +1126 -678
  99. package/{removeDialogAgentAction-OHMCNSSD.js → removeDialogAgentAction-GTX5AN54.js} +11 -11
  100. package/{runAgentClientLoop-AIGLXC3R.js → runAgentClientLoop-P25HACXX.js} +42 -40
  101. package/{runtimeDoctorCommands-LRHLPDRC.js → runtimeDoctorCommands-QNF3J3UV.js} +6 -6
  102. package/{setDialogExtraReferencesAction-YJUM5BWQ.js → setDialogExtraReferencesAction-WFQ63KYO.js} +7 -8
  103. package/{setPrimaryDialogAgentAction-EBAREZDM.js → setPrimaryDialogAgentAction-P57FXTSU.js} +11 -11
  104. package/{settingSlice-UW4IUI5L.js → settingSlice-CK5YZGNI.js} +8 -9
  105. package/skillDiscovery-ZYHSX7VD.js +16 -0
  106. package/{spaceCommands-BKHSPR73.js → spaceCommands-6O5EFEM5.js} +41 -33
  107. package/{spaceSlice-TYNWE3OC.js → spaceSlice-4EXTYXS4.js} +25 -24
  108. package/{streamAgentChatTurn-HRMFFA7E.js → streamAgentChatTurn-Q3FJAHG2.js} +205 -107
  109. package/{tableCommands-O7O2LBEG.js → tableCommands-E4SQWDOX.js} +10 -10
  110. package/{tools-3JNDS3WH.js → tools-AHNQE4GP.js} +33 -31
  111. package/{tuiSummaryLlmCaller-RV3THLZF.js → tuiSummaryLlmCaller-TXLF4MEI.js} +8 -6
  112. package/{updateCommands-VOSPKXFF.js → updateCommands-XZDOOYFH.js} +13 -5
  113. package/{updateDialogTitleAction-OCETSKCP.js → updateDialogTitleAction-XZDVLKVX.js} +25 -25
  114. package/chunk-5MMAK75Q.js +0 -15
  115. package/chunk-AZPCASRE.js +0 -36
  116. package/chunk-BBYVOSU2.js +0 -792
  117. package/chunk-DCOA4QSN.js +0 -105
  118. package/chunk-UAD2WT54.js +0 -64
  119. package/{chunk-DZ3XJVET.js → chunk-3QAF2LTL.js} +3 -3
  120. package/{chunk-X7W4TNB4.js → chunk-CMKUBIGW.js} +3 -3
  121. package/{chunk-4CQLVGRV.js → chunk-ID4G2MAR.js} +3 -3
  122. package/{chunk-5CS33IBL.js → chunk-PDL2AV3E.js} +3 -3
  123. package/{chunk-EHLHSX7R.js → chunk-TOILAPLH.js} +3 -3
  124. package/{chunk-UTLZINAP.js → chunk-TSUJOM6N.js} +10 -10
  125. package/{offlineMarxistsAgentCommand-BCPJITJL.js → offlineMarxistsAgentCommand-5OOWZLJI.js} +3 -3
@@ -3,25 +3,25 @@ import {
3
3
  finalizeAccumulatedToolCalls,
4
4
  flushChatCompletionStream,
5
5
  readSseFrames,
6
- throwIfChatCompletionStreamFailed,
7
- toOpenAiCompatibleMessages
8
- } from "./chunk-5KLGBAAR.js";
6
+ throwIfChatCompletionStreamFailed
7
+ } from "./chunk-ZR4VHQ5D.js";
9
8
  import {
10
9
  parseToolArgumentsJson
11
- } from "./chunk-6SLCD4RB.js";
10
+ } from "./chunk-4BTHUYMY.js";
12
11
  import {
13
12
  createThinkParserState,
14
13
  extractThinkContent
15
14
  } from "./chunk-OPZYGHUC.js";
16
15
  import {
17
16
  buildProviderAuthHeaders,
18
- buildProviderExecutionPlan
19
- } from "./chunk-JRKKFRAV.js";
17
+ buildProviderExecutionPlan,
18
+ toOpenAiCompatibleMessages
19
+ } from "./chunk-TSKWE74T.js";
20
20
  import {
21
21
  canonicalizeToolName,
22
22
  createGlob,
23
23
  resolveExecutableOnPath
24
- } from "./chunk-AUQ747NZ.js";
24
+ } from "./chunk-43UD5ZP7.js";
25
25
  import {
26
26
  toErrorMessage
27
27
  } from "./chunk-HNMXYYJR.js";
@@ -40,6 +40,9 @@ import {
40
40
  import {
41
41
  asTrimmedLowercaseString
42
42
  } from "./chunk-QSJ3HFEF.js";
43
+ import {
44
+ isRecord
45
+ } from "./chunk-KUG52IAP.js";
43
46
  import {
44
47
  asTrimmedString
45
48
  } from "./chunk-3EUW3WEO.js";
@@ -52,9 +55,6 @@ import {
52
55
  import {
53
56
  asOptionalTrimmedString
54
57
  } from "./chunk-G3SUCT4W.js";
55
- import {
56
- isRecord
57
- } from "./chunk-KUG52IAP.js";
58
58
 
59
59
  // packages/agent-runtime/kimiUserAgent.ts
60
60
  var KIMI_CODE_CLI_VERSION = "0.29.2";
@@ -389,10 +389,16 @@ function dialogMessageRecordToAgentRuntimeMessage(record) {
389
389
  return {
390
390
  role: record.role,
391
391
  content: record.content ?? "",
392
+ ...record.contextReference !== void 0 ? { context_reference: record.contextReference } : {},
392
393
  ...typeof record.reasoning_content === "string" ? { reasoning_content: record.reasoning_content } : {},
393
394
  ...typeof record.toolCallId === "string" ? { tool_call_id: record.toolCallId } : {},
394
395
  ...Array.isArray(record.tool_calls) ? { tool_calls: record.tool_calls } : {},
395
- ...typeof record.toolName === "string" ? { toolName: record.toolName } : {}
396
+ ...typeof record.toolName === "string" ? { toolName: record.toolName } : {},
397
+ ...(() => {
398
+ const raw = record.createdAt;
399
+ const ms = typeof raw === "number" ? raw : typeof raw === "string" ? Date.parse(raw) : NaN;
400
+ return Number.isFinite(ms) ? { createdAt: ms } : {};
401
+ })()
396
402
  };
397
403
  }
398
404
 
@@ -469,6 +475,7 @@ function buildDialogMessageWriteOps(args) {
469
475
  dialogId: args.dialogId,
470
476
  role: message.role,
471
477
  content: message.content ?? "",
478
+ ...message.context_reference !== void 0 ? { contextReference: message.context_reference } : {},
472
479
  // 持久化思维链(reasoning):空轮/异常排查的关键证据,
473
480
  // 回读由 dialogMessageRecordToAgentRuntimeMessage 还原。
474
481
  ...typeof message.reasoning_content === "string" ? { reasoning_content: message.reasoning_content } : {},
@@ -878,7 +885,14 @@ var DEFAULT_LOCAL_TOOLS = /* @__PURE__ */ new Set([
878
885
  "searchFiles",
879
886
  "execShell",
880
887
  "launchProcess",
881
- "listProcesses"
888
+ "listProcesses",
889
+ // Scheduling tools: forking/observing sub-agents is a declared capability
890
+ // of capable agents, and the blast radius is strictly narrower than
891
+ // execShell (already default-enabled). Requiring an env allowlist for these
892
+ // while execShell is open by default inverted the risk model and forced every
893
+ // new local user to hand-edit .env files before sub-agent tasks worked.
894
+ "startAgentRun",
895
+ "controlAgentRun"
882
896
  ]);
883
897
  function parseToolAllowlist(value) {
884
898
  return (value ?? "").split(",").flatMap((item) => {
@@ -951,7 +965,7 @@ function resolveLocalToolPolicy(args) {
951
965
  return {
952
966
  allowed: false,
953
967
  toolName,
954
- reason: `${toolName} is not enabled for local runtime runs. Set NOLO_LOCAL_ALLOWED_TOOLS and make sure the agent declares the tool before using it locally.`
968
+ reason: `${toolName} is not enabled for local runtime runs. The agent must declare the tool in its manifest; in restricted mode (NOLO_LOCAL_TOOL_MODE=restricted) non-default tools also require adding to NOLO_LOCAL_ALLOWED_TOOLS (e.g. NOLO_LOCAL_ALLOWED_TOOLS=someCustomTool) and restarting the session so the env var loads.`
955
969
  };
956
970
  }
957
971
  async function executeLocalToolWithPolicy(args) {
@@ -1331,7 +1345,7 @@ function buildSearchWorkspaceParameters(variant) {
1331
1345
  const path = buildWorkspacePathProperty();
1332
1346
  const includeIgnored = {
1333
1347
  type: "boolean",
1334
- description: "When true, search files ignored by .gitignore such as .tmp. Defaults to false; .git and node_modules remain excluded."
1348
+ description: "When true, search files ignored by .gitignore such as .tmp. Defaults to false. Build artifacts (dist/, *.tsbuildinfo, *.min.js, etc.) are always excluded regardless of this flag. .git and node_modules remain excluded."
1335
1349
  };
1336
1350
  const exclude = {
1337
1351
  type: "array",
@@ -1694,6 +1708,24 @@ var EXEC_SHELL_TIMEOUT_ENV = "NOLO_EXEC_SHELL_TIMEOUT_MS";
1694
1708
  var EXEC_SHELL_DETACH_ENV = "NOLO_EXEC_SHELL_DETACH_MS";
1695
1709
  var DEFAULT_EXEC_SHELL_DETACH_MS = 12e4;
1696
1710
  var DEFAULT_LOCAL_API_ORIGIN = "http://127.0.0.1:38123";
1711
+ var ALWAYS_EXCLUDED_GLOBS = [
1712
+ "*.tsbuildinfo",
1713
+ "dist/**",
1714
+ "build/**",
1715
+ "out/**",
1716
+ "coverage/**",
1717
+ ".next/**",
1718
+ ".turbo/**",
1719
+ "public/assets/**",
1720
+ "*.min.js",
1721
+ "*.min.css",
1722
+ "*.map",
1723
+ "*.lock",
1724
+ "bun.lockb",
1725
+ "*.ldb"
1726
+ ];
1727
+ var DEFAULT_SEARCH_MAX_RESULTS = 200;
1728
+ var SEARCH_OUTPUT_CHAR_LIMIT = 2e4;
1697
1729
  function readTrimmedString(value) {
1698
1730
  return asOptionalTrimmedString(value);
1699
1731
  }
@@ -2039,8 +2071,29 @@ function sliceReadFileContent(args) {
2039
2071
  truncated: startLine > 1 || effectiveEndLine < totalLines
2040
2072
  };
2041
2073
  }
2042
- function countExactTextOccurrences(args) {
2043
- return args.content.split(args.oldText).length - 1;
2074
+ function buildEolTolerantPattern(oldTextLf) {
2075
+ const escaped = oldTextLf.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2076
+ const pattern = escaped.replace(/\n/g, "\\r?\\n");
2077
+ return new RegExp(pattern, "g");
2078
+ }
2079
+ function countAndReplaceEolTolerant(args) {
2080
+ const oldTextLf = normalizeEolToLf(args.oldText);
2081
+ const newTextLf = normalizeEolToLf(args.newText);
2082
+ const pattern = buildEolTolerantPattern(oldTextLf);
2083
+ let count = 0;
2084
+ const result = args.content.replace(pattern, (match) => {
2085
+ count++;
2086
+ const eol = match.includes("\r\n") ? "\r\n" : "\n";
2087
+ return convertEol(newTextLf, eol);
2088
+ });
2089
+ return { count, result };
2090
+ }
2091
+ function normalizeEolToLf(text) {
2092
+ return text.replace(/\r\n/g, "\n");
2093
+ }
2094
+ function convertEol(text, eol) {
2095
+ const lf = normalizeEolToLf(text);
2096
+ return eol === "\r\n" ? lf.replace(/\n/g, "\r\n") : lf;
2044
2097
  }
2045
2098
  function pluralizeReplacement(count) {
2046
2099
  return count === 1 ? "replacement" : "replacements";
@@ -2061,8 +2114,8 @@ function requireWorkspaceGlobPattern(args) {
2061
2114
  if (!pattern) throw new Error("globFiles requires a non-empty pattern.");
2062
2115
  return pattern;
2063
2116
  }
2064
- function readWorkspaceMaxResults(args) {
2065
- if (args.maxResults === void 0) return void 0;
2117
+ function readWorkspaceMaxResults(args, defaultValue) {
2118
+ if (args.maxResults === void 0) return defaultValue;
2066
2119
  const value = Number(args.maxResults);
2067
2120
  if (!Number.isInteger(value) || value < 1) {
2068
2121
  throw new Error("maxResults must be a positive integer.");
@@ -2238,6 +2291,29 @@ function waitForNodeProcessExit(proc) {
2238
2291
  });
2239
2292
  });
2240
2293
  }
2294
+ function isSpawnFailureError(error) {
2295
+ if (!error || typeof error !== "object") return false;
2296
+ const err = error;
2297
+ if (err.code === "ENOENT") return true;
2298
+ const message = toErrorMessage(error).toLowerCase();
2299
+ return message.includes("enoent") || message.includes("executable not found") || message.includes("spawn") && message.includes("not found");
2300
+ }
2301
+ function spawnFailedCommandResult(error, outputLimit) {
2302
+ const stderr = `spawn failed: ${toErrorMessage(error)}
2303
+ `;
2304
+ return {
2305
+ stdout: "",
2306
+ stderr,
2307
+ exitCode: 127,
2308
+ timedOut: false,
2309
+ spawnFailed: true,
2310
+ content: truncateToolOutput(
2311
+ [`stderr:
2312
+ ${stderr.trim()}`, "exitCode: 127"].filter(Boolean).join("\n\n"),
2313
+ outputLimit
2314
+ )
2315
+ };
2316
+ }
2241
2317
  function resolveRipgrepBinary(env = process.env) {
2242
2318
  const fromEnv = asOptionalTrimmedString(env.NOLO_BUNDLED_RG);
2243
2319
  if (fromEnv) {
@@ -2248,6 +2324,9 @@ function resolveRipgrepBinary(env = process.env) {
2248
2324
  }
2249
2325
  return resolveExecutableOnPath("rg");
2250
2326
  }
2327
+ function resolveGrepBinary() {
2328
+ return resolveExecutableOnPath("grep");
2329
+ }
2251
2330
  async function runWorkspaceCommand(args) {
2252
2331
  const timeoutMs = asOptionalPositiveFiniteNumber(args.timeoutMs);
2253
2332
  const detached = process.platform !== "win32";
@@ -2332,7 +2411,21 @@ async function runWorkspaceCommand(args) {
2332
2411
  }
2333
2412
  if (abortPromise) racers.push(abortPromise);
2334
2413
  if (detachPromise) racers.push(detachPromise);
2335
- const raceWinner = await Promise.race(racers);
2414
+ let raceWinner;
2415
+ try {
2416
+ raceWinner = await Promise.race(racers);
2417
+ } catch (error) {
2418
+ if (timeout) clearTimeout(timeout);
2419
+ if (detachTimer) clearTimeout(detachTimer);
2420
+ if (abortListener && args.abortSignal) {
2421
+ args.abortSignal.removeEventListener("abort", abortListener);
2422
+ }
2423
+ detachSignalCleanup();
2424
+ if (isSpawnFailureError(error)) {
2425
+ return spawnFailedCommandResult(error, args.outputLimit);
2426
+ }
2427
+ throw error;
2428
+ }
2336
2429
  if (timeout) clearTimeout(timeout);
2337
2430
  if (detachTimer) clearTimeout(detachTimer);
2338
2431
  if (abortListener && args.abortSignal) {
@@ -2449,12 +2542,40 @@ async function runWorkspaceCommandLimitedLines(args) {
2449
2542
  ]);
2450
2543
  } catch (error) {
2451
2544
  const settled2 = await exitSettled;
2452
- if (!settled2.ok) throw settled2.error;
2545
+ if (!settled2.ok) {
2546
+ return {
2547
+ stdout: "",
2548
+ stderr: `spawn failed: ${toErrorMessage(settled2.error)}
2549
+ `,
2550
+ exitCode: 127,
2551
+ limitedByMaxResults: false,
2552
+ spawnFailed: true
2553
+ };
2554
+ }
2555
+ if (isSpawnFailureError(error)) {
2556
+ return {
2557
+ stdout: "",
2558
+ stderr: `spawn failed: ${toErrorMessage(error)}
2559
+ `,
2560
+ exitCode: 127,
2561
+ limitedByMaxResults: false,
2562
+ spawnFailed: true
2563
+ };
2564
+ }
2453
2565
  throw error;
2454
2566
  }
2455
2567
  if (!limitedByMaxResults && pending.trim()) lines.push(pending);
2456
2568
  const settled = await exitSettled;
2457
- if (!settled.ok) throw settled.error;
2569
+ if (!settled.ok) {
2570
+ return {
2571
+ stdout: "",
2572
+ stderr: `spawn failed: ${toErrorMessage(settled.error)}
2573
+ `,
2574
+ exitCode: 127,
2575
+ limitedByMaxResults: false,
2576
+ spawnFailed: true
2577
+ };
2578
+ }
2458
2579
  const exitCode = limitedByMaxResults ? 0 : settled.code;
2459
2580
  const stderr = await stderrPromise;
2460
2581
  return {
@@ -2583,13 +2704,16 @@ async function editFileTool(args) {
2583
2704
  ...args.confirmExternalFileAccess ? { confirmExternalFileAccess: args.confirmExternalFileAccess } : {}
2584
2705
  });
2585
2706
  const content = await readFile(absolutePath, "utf8");
2586
- const replacementCount = countExactTextOccurrences({ content, oldText });
2707
+ const { count: replacementCount, result: nextContent } = countAndReplaceEolTolerant({
2708
+ content,
2709
+ oldText,
2710
+ newText
2711
+ });
2587
2712
  if (replacementCount !== expectedReplacements) {
2588
2713
  throw new Error(
2589
- `editFile expected ${expectedReplacements} ${pluralizeReplacement(expectedReplacements)} but found ${replacementCount} in ${requestedPath}.`
2714
+ `editFile expected ${expectedReplacements} ${pluralizeReplacement(expectedReplacements)} but found ${replacementCount} in ${requestedPath} (matched after EOL normalization; verify the exact text).`
2590
2715
  );
2591
2716
  }
2592
- const nextContent = content.split(oldText).join(newText);
2593
2717
  await writeFile(absolutePath, nextContent, "utf8");
2594
2718
  const relativePath = normalizeWorkspaceRelativePath({
2595
2719
  workspaceRoot: resolve(args.workspaceRoot),
@@ -2686,7 +2810,7 @@ async function searchFilesTool(args) {
2686
2810
  const parsed = parseWorkspaceToolArguments(args.call.arguments);
2687
2811
  const query = requireWorkspaceSearchQuery(parsed);
2688
2812
  const exclude = readWorkspaceExcludeGlobs(parsed);
2689
- const maxResults = readWorkspaceMaxResults(parsed);
2813
+ const maxResults = readWorkspaceMaxResults(parsed, DEFAULT_SEARCH_MAX_RESULTS);
2690
2814
  const contextLines = readWorkspaceContextLines(parsed);
2691
2815
  const literal = parsed.literal === true;
2692
2816
  const caseSensitive = parsed.caseSensitive === false ? false : true;
@@ -2701,8 +2825,8 @@ async function searchFilesTool(args) {
2701
2825
  workspaceRoot: resolve(args.workspaceRoot),
2702
2826
  targetPath: searchPath
2703
2827
  });
2704
- const rgBinary = resolveRipgrepBinary();
2705
- const grepBinary = resolveExecutableOnPath("grep") || "grep";
2828
+ const rgBinary = (args.resolveRipgrepBinary ?? resolveRipgrepBinary)();
2829
+ const grepBinary = (args.resolveGrepBinary ?? resolveGrepBinary)();
2706
2830
  const rgCommand = rgBinary ? [
2707
2831
  rgBinary,
2708
2832
  "--line-number",
@@ -2716,29 +2840,26 @@ async function searchFilesTool(args) {
2716
2840
  "!node_modules",
2717
2841
  "--glob",
2718
2842
  "!.git",
2843
+ ...ALWAYS_EXCLUDED_GLOBS.flatMap((g) => ["--glob", `!${g}`]),
2719
2844
  ...exclude.flatMap((excludePattern) => ["--glob", `!${excludePattern}`]),
2720
2845
  "--",
2721
2846
  query,
2722
2847
  relativeSearchPath
2723
2848
  ] : null;
2724
- const grepCommand = [
2725
- grepBinary,
2726
- "-R",
2727
- "-n",
2728
- "-I",
2729
- ...literal ? ["-F"] : [],
2730
- ...caseSensitive ? [] : ["-i"],
2731
- ...contextLines !== void 0 ? ["-C", String(contextLines)] : [],
2732
- "--exclude-dir=node_modules",
2733
- "--exclude-dir=.git",
2734
- ...exclude.flatMap((excludePattern) => ["--exclude", excludePattern]),
2735
- query,
2736
- relativeSearchPath
2849
+ const grepExcludeArgs = [
2850
+ ...ALWAYS_EXCLUDED_GLOBS.flatMap((g) => {
2851
+ if (!g.endsWith("/**")) return ["--exclude", g];
2852
+ const dir = g.replace(/\/\*\*$/, "");
2853
+ return dir.includes("/") ? [] : [`--exclude-dir=${dir}`];
2854
+ }),
2855
+ ...exclude.flatMap((excludePattern) => ["--exclude", excludePattern])
2737
2856
  ];
2857
+ let searchEngine = "js";
2858
+ let binariesUnavailable = !rgBinary && !grepBinary;
2738
2859
  const result = await (async () => {
2739
2860
  if (rgCommand) {
2740
2861
  try {
2741
- return maxResults && !contextLines ? await runWorkspaceCommandLimitedLines({
2862
+ const rgResult = maxResults && !contextLines ? await runWorkspaceCommandLimitedLines({
2742
2863
  workspaceRoot: args.workspaceRoot,
2743
2864
  command: rgCommand,
2744
2865
  maxLines: maxResults
@@ -2746,35 +2867,66 @@ async function searchFilesTool(args) {
2746
2867
  workspaceRoot: args.workspaceRoot,
2747
2868
  command: rgCommand
2748
2869
  });
2870
+ if (!rgResult.spawnFailed) {
2871
+ searchEngine = "ripgrep";
2872
+ return rgResult;
2873
+ }
2749
2874
  } catch {
2750
2875
  }
2751
2876
  }
2752
- try {
2753
- return maxResults && !contextLines ? await runWorkspaceCommandLimitedLines({
2754
- workspaceRoot: args.workspaceRoot,
2755
- command: grepCommand,
2756
- maxLines: maxResults
2757
- }) : await runWorkspaceCommand({
2758
- workspaceRoot: args.workspaceRoot,
2759
- command: grepCommand
2760
- });
2761
- } catch {
2762
- return {
2763
- stdout: "",
2764
- stderr: "",
2765
- exitCode: 0,
2766
- limitedByMaxResults: false
2767
- };
2877
+ if (grepBinary) {
2878
+ const grepCommand = [
2879
+ grepBinary,
2880
+ "-R",
2881
+ "-n",
2882
+ "-I",
2883
+ ...literal ? ["-F"] : [],
2884
+ ...caseSensitive ? [] : ["-i"],
2885
+ ...contextLines !== void 0 ? ["-C", String(contextLines)] : [],
2886
+ "--exclude-dir=node_modules",
2887
+ "--exclude-dir=.git",
2888
+ ...grepExcludeArgs,
2889
+ query,
2890
+ relativeSearchPath
2891
+ ];
2892
+ try {
2893
+ const grepResult = maxResults && !contextLines ? await runWorkspaceCommandLimitedLines({
2894
+ workspaceRoot: args.workspaceRoot,
2895
+ command: grepCommand,
2896
+ maxLines: maxResults
2897
+ }) : await runWorkspaceCommand({
2898
+ workspaceRoot: args.workspaceRoot,
2899
+ command: grepCommand
2900
+ });
2901
+ if (!grepResult.spawnFailed) {
2902
+ searchEngine = "grep";
2903
+ return grepResult;
2904
+ }
2905
+ binariesUnavailable = true;
2906
+ } catch {
2907
+ binariesUnavailable = true;
2908
+ }
2909
+ } else {
2910
+ binariesUnavailable = true;
2768
2911
  }
2912
+ return {
2913
+ stdout: "",
2914
+ stderr: "",
2915
+ exitCode: 0,
2916
+ limitedByMaxResults: false,
2917
+ spawnFailed: true
2918
+ };
2769
2919
  })();
2770
2920
  let outputLines = result.stdout.split(/\r?\n/).map((line) => line.trimEnd().replace(/^\.\//, "")).filter(Boolean);
2921
+ outputLines = dropAlwaysExcludedLines(outputLines);
2771
2922
  let limitedByMaxResults = "limitedByMaxResults" in result && result.limitedByMaxResults === true;
2772
- if (outputLines.length === 0 && result.exitCode === 0 && (includeIgnored || !await hasRootGitignore(args.workspaceRoot))) {
2923
+ const forceJsFallback = binariesUnavailable || result.spawnFailed === true;
2924
+ if (outputLines.length === 0 && (forceJsFallback || result.exitCode === 0 && (includeIgnored || !await hasRootGitignore(args.workspaceRoot)))) {
2773
2925
  const fallback = await scanWorkspaceTextMatches({
2774
2926
  workspaceRoot: args.workspaceRoot,
2775
2927
  relativeSearchPath,
2776
2928
  query,
2777
- exclude,
2929
+ exclude: [...ALWAYS_EXCLUDED_GLOBS, ...exclude],
2778
2930
  maxResults,
2779
2931
  literal,
2780
2932
  caseSensitive,
@@ -2782,6 +2934,7 @@ async function searchFilesTool(args) {
2782
2934
  });
2783
2935
  outputLines = fallback.lines;
2784
2936
  limitedByMaxResults = fallback.limitedByMaxResults;
2937
+ searchEngine = "js";
2785
2938
  }
2786
2939
  if (contextLines && outputLines.length > 0) {
2787
2940
  const limited = limitSearchOutputByMatches(outputLines, maxResults);
@@ -2793,8 +2946,11 @@ async function searchFilesTool(args) {
2793
2946
  const match = line.match(/^(.*?):\d+:/);
2794
2947
  return match?.[1] ? [match[1]] : [];
2795
2948
  })));
2949
+ const rawContent = outputLines.join("\n");
2950
+ const content = truncateToolOutput(rawContent, SEARCH_OUTPUT_CHAR_LIMIT);
2951
+ const truncatedByByteLimit = content.length !== rawContent.length;
2796
2952
  return {
2797
- content: outputLines.join("\n"),
2953
+ content,
2798
2954
  metadata: {
2799
2955
  query,
2800
2956
  path: requestedPath,
@@ -2807,10 +2963,12 @@ async function searchFilesTool(args) {
2807
2963
  count: outputLines.length,
2808
2964
  matchCount: matchLines.length,
2809
2965
  matchedFiles,
2810
- truncated: limitedByMaxResults,
2966
+ truncated: limitedByMaxResults || truncatedByByteLimit,
2811
2967
  limitedByMaxResults,
2968
+ ...truncatedByByteLimit ? { truncatedByByteLimit: true } : {},
2812
2969
  ...maxResults ? { maxResults } : {},
2813
2970
  exitCode: result.exitCode,
2971
+ searchEngine,
2814
2972
  ...extractActivity(parsed) ? { activity: extractActivity(parsed) } : {}
2815
2973
  }
2816
2974
  };
@@ -2841,6 +2999,14 @@ async function filterRootGitignoredFiles(args) {
2841
2999
  const globs = patterns.map((pattern) => createGlob(pattern));
2842
3000
  return args.files.filter((file) => !globs.some((glob) => glob.match(file)));
2843
3001
  }
3002
+ function dropAlwaysExcludedLines(lines) {
3003
+ const globs = ALWAYS_EXCLUDED_GLOBS.map((pattern) => createGlob(pattern));
3004
+ return lines.filter((line) => {
3005
+ const filePath = line.match(/^(.*?):\d+[:-]/)?.[1];
3006
+ if (!filePath) return true;
3007
+ return !globs.some((glob) => glob.match(filePath));
3008
+ });
3009
+ }
2844
3010
  function scanWorkspaceGlobFiles(args) {
2845
3011
  const pathPrefix = args.relativeSearchPath === "." ? "" : `${args.relativeSearchPath.replace(/\/+$/, "")}/`;
2846
3012
  return Array.from(createGlob(args.pattern).scanSync({
@@ -2933,38 +3099,76 @@ async function globFilesTool(args) {
2933
3099
  workspaceRoot: resolve(args.workspaceRoot),
2934
3100
  targetPath: searchPath
2935
3101
  });
2936
- const command = [
2937
- "rg",
2938
- "--files",
2939
- "--hidden",
2940
- ...includeIgnored ? ["--no-ignore"] : [],
2941
- "--glob",
2942
- pattern,
2943
- "--glob",
2944
- "!node_modules",
2945
- "--glob",
2946
- "!.git",
2947
- ...exclude.flatMap((excludePattern) => ["--glob", `!${excludePattern}`]),
2948
- ...relativeSearchPath === "." ? [] : [relativeSearchPath]
2949
- ];
2950
- const result = await runWorkspaceCommand({
2951
- workspaceRoot: args.workspaceRoot,
2952
- command
2953
- });
2954
3102
  const pathPrefix = relativeSearchPath === "." ? "" : `${relativeSearchPath.replace(/\/+$/, "")}/`;
2955
- let files = result.stdout.split(/\r?\n/).map((line) => line.trim().replace(/^\.\//, "")).filter(Boolean).filter((line) => !pathPrefix || line === relativeSearchPath || line.startsWith(pathPrefix)).sort((left, right) => left.localeCompare(right));
2956
- files = await filterRootGitignoredFiles({
2957
- workspaceRoot: args.workspaceRoot,
2958
- files,
2959
- includeIgnored
2960
- });
2961
- if (files.length === 0 && (includeIgnored || !await hasRootGitignore(args.workspaceRoot))) {
3103
+ const rgBinary = (args.resolveRipgrepBinary ?? resolveRipgrepBinary)();
3104
+ let searchEngine = "js";
3105
+ let exitCode = 0;
3106
+ let files = [];
3107
+ if (rgBinary) {
3108
+ const command = [
3109
+ rgBinary,
3110
+ "--files",
3111
+ "--hidden",
3112
+ ...includeIgnored ? ["--no-ignore"] : [],
3113
+ "--glob",
3114
+ pattern,
3115
+ "--glob",
3116
+ "!node_modules",
3117
+ "--glob",
3118
+ "!.git",
3119
+ ...exclude.flatMap((excludePattern) => ["--glob", `!${excludePattern}`]),
3120
+ ...relativeSearchPath === "." ? [] : [relativeSearchPath]
3121
+ ];
3122
+ const result = await runWorkspaceCommand({
3123
+ workspaceRoot: args.workspaceRoot,
3124
+ command
3125
+ });
3126
+ exitCode = result.exitCode;
3127
+ if (result.spawnFailed) {
3128
+ files = scanWorkspaceGlobFiles({
3129
+ workspaceRoot: args.workspaceRoot,
3130
+ pattern,
3131
+ relativeSearchPath,
3132
+ exclude
3133
+ });
3134
+ files = await filterRootGitignoredFiles({
3135
+ workspaceRoot: args.workspaceRoot,
3136
+ files,
3137
+ includeIgnored
3138
+ });
3139
+ searchEngine = "js";
3140
+ } else {
3141
+ files = result.stdout.split(/\r?\n/).map((line) => line.trim().replace(/^\.\//, "")).filter(Boolean).filter((line) => !pathPrefix || line === relativeSearchPath || line.startsWith(pathPrefix)).sort((left, right) => left.localeCompare(right));
3142
+ files = await filterRootGitignoredFiles({
3143
+ workspaceRoot: args.workspaceRoot,
3144
+ files,
3145
+ includeIgnored
3146
+ });
3147
+ if (files.length === 0 && (includeIgnored || !await hasRootGitignore(args.workspaceRoot))) {
3148
+ files = scanWorkspaceGlobFiles({
3149
+ workspaceRoot: args.workspaceRoot,
3150
+ pattern,
3151
+ relativeSearchPath,
3152
+ exclude
3153
+ });
3154
+ searchEngine = "js";
3155
+ } else {
3156
+ searchEngine = "ripgrep";
3157
+ }
3158
+ }
3159
+ } else {
2962
3160
  files = scanWorkspaceGlobFiles({
2963
3161
  workspaceRoot: args.workspaceRoot,
2964
3162
  pattern,
2965
3163
  relativeSearchPath,
2966
3164
  exclude
2967
3165
  });
3166
+ files = await filterRootGitignoredFiles({
3167
+ workspaceRoot: args.workspaceRoot,
3168
+ files,
3169
+ includeIgnored
3170
+ });
3171
+ searchEngine = "js";
2968
3172
  }
2969
3173
  const commandLimitedByMaxResults = false;
2970
3174
  const totalCount = commandLimitedByMaxResults ? void 0 : files.length;
@@ -2985,7 +3189,8 @@ async function globFilesTool(args) {
2985
3189
  truncated: limitedByMaxResults,
2986
3190
  limitedByMaxResults,
2987
3191
  ...maxResults ? { maxResults } : {},
2988
- exitCode: result.exitCode,
3192
+ exitCode,
3193
+ searchEngine,
2989
3194
  ...activity ? { activity } : {}
2990
3195
  }
2991
3196
  };
@@ -3189,6 +3394,10 @@ async function listProcessesTool(args) {
3189
3394
  }
3190
3395
  function createLocalWorkspaceToolExecutors(args) {
3191
3396
  const fileAccess = args.confirmExternalFileAccess ? { confirmExternalFileAccess: args.confirmExternalFileAccess } : {};
3397
+ const searchBinaryResolvers = {
3398
+ ...args.resolveRipgrepBinary ? { resolveRipgrepBinary: args.resolveRipgrepBinary } : {},
3399
+ ...args.resolveGrepBinary ? { resolveGrepBinary: args.resolveGrepBinary } : {}
3400
+ };
3192
3401
  return {
3193
3402
  editFile: (call) => editFileTool({
3194
3403
  call,
@@ -3198,7 +3407,8 @@ function createLocalWorkspaceToolExecutors(args) {
3198
3407
  globFiles: (call) => globFilesTool({
3199
3408
  call,
3200
3409
  workspaceRoot: args.workspaceRoot,
3201
- ...fileAccess
3410
+ ...fileAccess,
3411
+ ...args.resolveRipgrepBinary ? { resolveRipgrepBinary: args.resolveRipgrepBinary } : {}
3202
3412
  }),
3203
3413
  listFiles: (call) => listFilesTool({
3204
3414
  call,
@@ -3218,7 +3428,8 @@ function createLocalWorkspaceToolExecutors(args) {
3218
3428
  searchFiles: (call) => searchFilesTool({
3219
3429
  call,
3220
3430
  workspaceRoot: args.workspaceRoot,
3221
- ...fileAccess
3431
+ ...fileAccess,
3432
+ ...searchBinaryResolvers
3222
3433
  }),
3223
3434
  captureVisualState: (call) => captureVisualStateTool({
3224
3435
  call,