nolo-cli 0.11.0 → 0.12.0-alpha.10

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 (123) hide show
  1. package/{_executeModel-ZEDPX6OF.js → _executeModel-4F7VZR5U.js} +52 -50
  2. package/{action-AA4IJ5K2.js → action-OTOWUMPW.js} +8 -9
  3. package/{addContentAction-QYVO4SYP.js → addContentAction-JGBUVR7K.js} +7 -9
  4. package/{addDialogAgentAction-WVVKT5WQ.js → addDialogAgentAction-V6CL2UCF.js} +9 -10
  5. package/{agentDeleteCommand-RDPGYIWF.js → agentDeleteCommand-MMRSG6AB.js} +39 -28
  6. package/{agentEmailCommands-2HJJKS5F.js → agentEmailCommands-YFVKOGM7.js} +65 -62
  7. package/{agentListCommands-HFEVV3ZW.js → agentListCommands-2PZHIQ3I.js} +128 -31
  8. package/{agentMachineCommands-25ZOENJU.js → agentMachineCommands-55UUZNHF.js} +38 -26
  9. package/{agentPullCommand-GOL2S4W2.js → agentPullCommand-KRBRIFIY.js} +7 -7
  10. package/{agentRecordCommands-GTCOAM3L.js → agentRecordCommands-BTP3OAPL.js} +57 -54
  11. package/{agentRunCommand-YBS3JDLD.js → agentRunCommand-AA4QLRQZ.js} +60 -59
  12. package/{agentRunControl-K4VKNEHF.js → agentRunControl-IX4F5W2C.js} +3 -3
  13. package/{agentRuntimeLocal-UQBMQI2M.js → agentRuntimeLocal-QDJCBXM2.js} +31 -15
  14. package/{agentSlice-6EKOBCCS.js → agentSlice-6JAJOU3X.js} +8 -9
  15. package/{authCommand-O2OWACN4.js → authCommand-DMW336GT.js} +15 -15
  16. package/{authCommands-H7365TZP.js → authCommands-GH5T6RYV.js} +6 -6
  17. package/{chunk-E4HNJSHC.js → chunk-2BVGERJ2.js} +81 -19
  18. package/{chunk-LP65VRCI.js → chunk-3MI5LDDU.js} +6 -6
  19. package/{chunk-OPZYGHUC.js → chunk-3WGLLTK4.js} +59 -1
  20. package/{chunk-KJR5KAUQ.js → chunk-3XF6LCCN.js} +20 -7
  21. package/{chunk-PXZAAB4L.js → chunk-4LG74DRG.js} +4 -4
  22. package/{chunk-2FTTDCD4.js → chunk-4ON5AV72.js} +2 -1
  23. package/{chunk-IWX4UW2E.js → chunk-524BDL33.js} +89 -8
  24. package/{chunk-ISNCLGUZ.js → chunk-5NBHQD4D.js} +4 -3
  25. package/{chunk-ZPRK73XY.js → chunk-5S4SWV5G.js} +51 -38
  26. package/{chunk-EVZKZF32.js → chunk-5V524MHX.js} +195 -78
  27. package/{chunk-CKOJQDKR.js → chunk-7JJCGV7N.js} +146 -3
  28. package/{chunk-NEM67FX7.js → chunk-ARSC56DC.js} +1 -1
  29. package/{chunk-AFON4QUU.js → chunk-AS3ZQ3O2.js} +19 -28
  30. package/{chunk-4SOKXIW4.js → chunk-B4KT7RN2.js} +10 -13
  31. package/chunk-BYXBJQAS.js +0 -0
  32. package/{chunk-PXQN7IBQ.js → chunk-C3BFP57W.js} +95 -2
  33. package/{chunk-AEZAORLG.js → chunk-CGDQDXKZ.js} +5 -5
  34. package/{chunk-MQI77IRB.js → chunk-DPFWKRHG.js} +2 -2
  35. package/{chunk-TACHTH5D.js → chunk-EIMX5ER5.js} +18 -18
  36. package/{chunk-5MOUAAMK.js → chunk-FSUZIGIG.js} +197 -2578
  37. package/{chunk-FXXSGWAJ.js → chunk-H5Y2H2N6.js} +1 -1
  38. package/chunk-IKXL2HZ3.js +70 -0
  39. package/{chunk-QNMFBBZK.js → chunk-IVAOVLM7.js} +1 -1
  40. package/chunk-JFANOO7I.js +2274 -0
  41. package/{chunk-YPRZSPF3.js → chunk-JKWWVF2I.js} +1 -1
  42. package/{chunk-3NSIQZZ6.js → chunk-JU7J7EAV.js} +45 -45
  43. package/{chunk-X2S7JTIG.js → chunk-KWBH4RAL.js} +1280 -137
  44. package/{chunk-OUI3ZHYD.js → chunk-LUKQQJFL.js} +39 -2
  45. package/{chunk-BX6U3ETE.js → chunk-MG6BVV3T.js} +320 -102
  46. package/{chunk-L53FGHA4.js → chunk-MQRZ5QUH.js} +1 -1
  47. package/{chunk-2W2VYVI5.js → chunk-MZHSAPZP.js} +675 -177
  48. package/{chunk-YOCHZD7D.js → chunk-NCGFB7NN.js} +5 -3
  49. package/{chunk-DWGAEFJL.js → chunk-O4FOPRCP.js} +1 -1
  50. package/chunk-OGGHETUE.js +4900 -0
  51. package/{chunk-PNEW2Z3T.js → chunk-OHEKYYJF.js} +7 -7
  52. package/chunk-OOIN7HSO.js +146 -0
  53. package/{chunk-SV5MWASH.js → chunk-OPM2DE2J.js} +12 -12
  54. package/{chunk-5OIFWVX7.js → chunk-QJSTNKJY.js} +1 -1
  55. package/{chunk-C4FTUXVF.js → chunk-QLUFR47U.js} +11 -1
  56. package/{chunk-LAX2JSTX.js → chunk-QUHUVG6Y.js} +20 -2
  57. package/{chunk-D6OQHXVG.js → chunk-RQSOAMPJ.js} +22 -6
  58. package/{chunk-ZEYDOXHF.js → chunk-RUGEW4P7.js} +1515 -884
  59. package/{chunk-GBDSZATH.js → chunk-RWO5DGXI.js} +3 -3
  60. package/{chunk-S3GS2PRL.js → chunk-TA7VKOHU.js} +613 -70
  61. package/{chunk-XYZA7S7V.js → chunk-TQYYGE6G.js} +6 -6
  62. package/{chunk-YTBMA7NO.js → chunk-TYPHUVK5.js} +70 -8
  63. package/{chunk-4ZDM73SD.js → chunk-V2J635L5.js} +1 -1
  64. package/{chunk-C2MDQQ4O.js → chunk-VBSWHE5W.js} +1 -1
  65. package/{chunk-O26GI2JP.js → chunk-VKG4HXYV.js} +39 -8
  66. package/{chunk-IPYUUY4E.js → chunk-WPHL6ALY.js} +1 -1
  67. package/{chunk-7D3JY6C2.js → chunk-WVTBAALN.js} +64 -5
  68. package/{chunk-XFOLQLDN.js → chunk-XHQWJUW4.js} +2 -2
  69. package/{chunk-Q3UKJVU4.js → chunk-Y5P3ZRGC.js} +10 -10
  70. package/{chunk-KQQVGRA3.js → chunk-YUMYYXPI.js} +53 -183
  71. package/cliEnvHelpers-VYC2OPTB.js +30 -0
  72. package/{createAgentAutomationAction-LTI5QU35.js → createAgentAutomationAction-4NY4JLOK.js} +26 -26
  73. package/{createDialogAction-OJJ7L3L3.js → createDialogAction-I5WUWFJ6.js} +26 -26
  74. package/{createPageAction-5MXWVRGY.js → createPageAction-HFTIQ7TW.js} +26 -26
  75. package/{dbSlice-RQFQQVHT.js → dbSlice-J4PUZ6Y5.js} +6 -7
  76. package/{deleteFile-7XP5BGC7.js → deleteFile-M7UNIUOO.js} +4 -4
  77. package/{dialogCommands-JTJ3RPPW.js → dialogCommands-SVXRJFZS.js} +34 -23
  78. package/{dialogSlice-OWROKURJ.js → dialogSlice-35LA3ZRG.js} +26 -26
  79. package/{docCreateCommands-WJBEW2V7.js → docCreateCommands-Z7RIAYKQ.js} +34 -23
  80. package/{docDeleteCommands-BVEHJOY5.js → docDeleteCommands-NEYHUVZF.js} +35 -25
  81. package/{docReadCommands-NEWOZIJV.js → docReadCommands-I4US2SDE.js} +34 -23
  82. package/{docStore-3DXRYBMD.js → docStore-3CP2IO6M.js} +25 -25
  83. package/{docUpdateCommands-C7WHT7UU.js → docUpdateCommands-UU5HQBRP.js} +34 -23
  84. package/fetchMessages-KTNM7QAR.js +1 -1
  85. package/{getPricing-3E6UNPKL.js → getPricing-UGDJHI7M.js} +7 -3
  86. package/{handleSendMessageAction-B5PRR7BE.js → handleSendMessageAction-FWTLQGOZ.js} +30 -30
  87. package/index.js +77 -76
  88. package/{localDialogRead-YSKFFRTT.js → localDialogRead-2VRI24F4.js} +1 -1
  89. package/{machineCommands-IIIREAEK.js → machineCommands-STHZKBDJ.js} +64 -61
  90. package/{machineWsRunDispatch-3SIU6ILW.js → machineWsRunDispatch-7YHT4TPT.js} +56 -53
  91. package/{memoryCommands-O4MIPIWQ.js → memoryCommands-AFMES3LN.js} +5 -5
  92. package/{models-4OPOOLFS.js → models-LSCRJJ6P.js} +2 -2
  93. package/package.json +1 -1
  94. package/{read-QJNKRROM.js → read-2MIMRBZ2.js} +4 -4
  95. package/{readDocTool-H6X6KO6X.js → readDocTool-QHVGQHUJ.js} +1 -1
  96. package/{readlineWorkspace-HGP3I3OC.js → readlineWorkspace-OMRWYMB6.js} +1279 -716
  97. package/{removeDialogAgentAction-TCT35SOF.js → removeDialogAgentAction-XUZYQBV4.js} +13 -13
  98. package/{runAgentClientLoop-EFM4PTVI.js → runAgentClientLoop-FP2K33ZO.js} +47 -46
  99. package/{runtimeDoctorCommands-ID43Z7VX.js → runtimeDoctorCommands-QNF3J3UV.js} +6 -6
  100. package/{setDialogExtraReferencesAction-CGYLZMFH.js → setDialogExtraReferencesAction-PTAIFKGM.js} +9 -10
  101. package/{setPrimaryDialogAgentAction-F6BFQDJU.js → setPrimaryDialogAgentAction-HRFTDB44.js} +13 -13
  102. package/{settingSlice-2I5HMG47.js → settingSlice-VGRJUSE5.js} +7 -8
  103. package/{skillDiscovery-UGSQGX2U.js → skillDiscovery-6M2BZUFN.js} +3 -3
  104. package/{spaceCommands-MT7BWCIL.js → spaceCommands-GN73BCOB.js} +38 -29
  105. package/{spaceSlice-BS5TSW3W.js → spaceSlice-WTFNKOVA.js} +30 -26
  106. package/{streamAgentChatTurn-I3VBFNR3.js → streamAgentChatTurn-HBAQQVNK.js} +442 -130
  107. package/{toolApiClient-C7P7YSE4.js → toolApiClient-QI3YBNWB.js} +2 -2
  108. package/{tools-XSD2BOEN.js → tools-TPQYTLQZ.js} +34 -33
  109. package/{tuiSummaryLlmCaller-32NRHTT2.js → tuiSummaryLlmCaller-XDC7WG4K.js} +11 -7
  110. package/{updateCommands-VOSPKXFF.js → updateCommands-XZDOOYFH.js} +13 -5
  111. package/{updateDialogTitleAction-7FYNJOGP.js → updateDialogTitleAction-AWXNPUQJ.js} +30 -29
  112. package/chunk-AZPCASRE.js +0 -36
  113. package/chunk-BBYVOSU2.js +0 -792
  114. package/chunk-DCOA4QSN.js +0 -105
  115. package/chunk-UAD2WT54.js +0 -64
  116. package/{chunk-JK7VW2FN.js → chunk-5MIDPSSK.js} +3 -3
  117. package/{chunk-5CS33IBL.js → chunk-PDL2AV3E.js} +3 -3
  118. package/{chunk-5MMZUWSG.js → chunk-TOILAPLH.js} +5 -5
  119. package/{chunk-OOM6SJNR.js → chunk-TSUJOM6N.js} +6 -6
  120. package/{db-6QED3ZVO.js → db-HCJ5PRB5.js} +3 -3
  121. package/{offlineMarxistsAgentCommand-BCPJITJL.js → offlineMarxistsAgentCommand-5OOWZLJI.js} +3 -3
  122. package/{tableCommands-M3DJUETS.js → tableCommands-E4SQWDOX.js} +6 -6
  123. package/{understanding-ZOESBLF3.js → understanding-3CUQQ6SV.js} +3 -3
@@ -1,27 +1,36 @@
1
1
  import {
2
2
  applyChatCompletionDelta,
3
- finalizeAccumulatedToolCalls,
4
3
  flushChatCompletionStream,
4
+ providerHttpFailure,
5
5
  readSseFrames,
6
- throwIfChatCompletionStreamFailed,
7
- toOpenAiCompatibleMessages
8
- } from "./chunk-ZPRK73XY.js";
6
+ throwIfChatCompletionStreamFailed
7
+ } from "./chunk-5S4SWV5G.js";
9
8
  import {
10
9
  parseToolArgumentsJson
11
- } from "./chunk-S3GS2PRL.js";
10
+ } from "./chunk-TA7VKOHU.js";
12
11
  import {
13
12
  createThinkParserState,
14
- extractThinkContent
15
- } from "./chunk-OPZYGHUC.js";
13
+ createToolCallAccumulator,
14
+ extractThinkContent,
15
+ finalizeAccumulatedToolCalls
16
+ } from "./chunk-3WGLLTK4.js";
17
+ import {
18
+ requiresBareImageUrl,
19
+ toBareImageUrlMessages
20
+ } from "./chunk-LUKQQJFL.js";
16
21
  import {
17
22
  buildProviderAuthHeaders,
18
- buildProviderExecutionPlan
19
- } from "./chunk-YTBMA7NO.js";
23
+ buildProviderExecutionPlan,
24
+ toOpenAiCompatibleMessages
25
+ } from "./chunk-TYPHUVK5.js";
20
26
  import {
21
27
  canonicalizeToolName,
22
28
  createGlob,
23
29
  resolveExecutableOnPath
24
- } from "./chunk-4SOKXIW4.js";
30
+ } from "./chunk-B4KT7RN2.js";
31
+ import {
32
+ toErrorMessage
33
+ } from "./chunk-HNMXYYJR.js";
25
34
  import {
26
35
  asRecordOrEmpty
27
36
  } from "./chunk-KX6YBIHG.js";
@@ -38,17 +47,14 @@ import {
38
47
  asTrimmedLowercaseString
39
48
  } from "./chunk-QSJ3HFEF.js";
40
49
  import {
41
- toErrorMessage
42
- } from "./chunk-HNMXYYJR.js";
50
+ isRecord
51
+ } from "./chunk-KUG52IAP.js";
43
52
  import {
44
53
  asTrimmedString
45
54
  } from "./chunk-3EUW3WEO.js";
46
55
  import {
47
56
  asOptionalPositiveFiniteNumber
48
57
  } from "./chunk-U6VC4MU4.js";
49
- import {
50
- isRecord
51
- } from "./chunk-KUG52IAP.js";
52
58
  import {
53
59
  normalizeServerOrigin
54
60
  } from "./chunk-CF7PE763.js";
@@ -81,9 +87,14 @@ function parseSseDataLineJson(line) {
81
87
 
82
88
  // packages/agent-runtime/openAiCompatibleProvider.ts
83
89
  function buildOpenAiCompatibleChatCompletionRequest(args) {
90
+ const messages = toOpenAiCompatibleMessages(args.messages);
84
91
  const body = {
85
92
  model: args.providerConfig.model,
86
- messages: toOpenAiCompatibleMessages(args.messages),
93
+ messages: requiresBareImageUrl({
94
+ endpoint: args.providerConfig.endpoint,
95
+ provider: args.providerConfig.provider,
96
+ model: args.providerConfig.model
97
+ }) ? toBareImageUrlMessages(messages) : messages,
87
98
  stream: args.stream ?? false,
88
99
  ...args.providerConfig.requestOptions,
89
100
  ...args.tools && args.tools.length > 0 ? { tools: args.tools } : {},
@@ -121,6 +132,9 @@ function parseOpenAiCompatibleChatCompletionResponse(args) {
121
132
  ...reasoning ? { reasoning_content: reasoning } : {},
122
133
  ...typeof choiceMessage?.reasoning_content === "string" && choiceMessage.reasoning_content ? { reasoning_content: choiceMessage.reasoning_content } : {},
123
134
  ...typeof rawFinishReason === "string" && rawFinishReason.length > 0 ? { finish_reason: rawFinishReason } : {},
135
+ // 非流式:拿到完整的 200 JSON body 本身就证明这次调用走完了,
136
+ // 不必等 finish_reason(有从不发它的上游,见 AgentRuntimeResult)。
137
+ stream_complete: true,
124
138
  usage: args.data?.usage,
125
139
  trace: args.trace
126
140
  };
@@ -137,7 +151,7 @@ async function readOpenAiCompatibleSseCompletion(args) {
137
151
  content: "",
138
152
  reasoning: "",
139
153
  usage: void 0,
140
- accumulatedToolCalls: {},
154
+ accumulatedToolCalls: createToolCallAccumulator(),
141
155
  thinkState: createThinkParserState(),
142
156
  onTextDelta: args.onTextDelta,
143
157
  onReasoningDelta: args.onReasoningDelta
@@ -152,7 +166,7 @@ async function readOpenAiCompatibleSseCompletion(args) {
152
166
  content: state.content,
153
167
  ...state.reasoning ? { reasoning_content: state.reasoning } : {},
154
168
  ...tool_calls.length > 0 ? { tool_calls } : {},
155
- ...state.usage ? { usage: state.usage } : {},
169
+ ...state.usage ? { usage: state.usage, stream_complete: true } : {},
156
170
  ...state.finishReason ? { finish_reason: state.finishReason } : {}
157
171
  };
158
172
  }
@@ -183,13 +197,12 @@ async function executeOpenAiCompatibleChatCompletion(args) {
183
197
  }
184
198
  }
185
199
  if (!res.ok) {
186
- const raw2 = await res.text().catch(() => "");
187
- let data2 = raw2;
188
- try {
189
- data2 = JSON.parse(raw2);
190
- } catch {
191
- }
192
- throw new Error(`local provider failed: HTTP ${res.status} ${JSON.stringify(data2)}`);
200
+ throw providerHttpFailure({
201
+ label: "local provider",
202
+ status: res.status,
203
+ raw: await res.text().catch(() => ""),
204
+ messages: args.messages
205
+ });
193
206
  }
194
207
  const contentType = res.headers.get("content-type") ?? "";
195
208
  const isEventStream = contentType.includes("text/event-stream");
@@ -206,6 +219,7 @@ async function executeOpenAiCompatibleChatCompletion(args) {
206
219
  ...streamed.tool_calls ? { tool_calls: streamed.tool_calls } : {},
207
220
  ...streamed.reasoning_content ? { reasoning_content: streamed.reasoning_content } : {},
208
221
  ...streamed.usage ? { usage: streamed.usage } : {},
222
+ ...streamed.stream_complete ? { stream_complete: true } : {},
209
223
  ...streamed.finish_reason ? { finish_reason: streamed.finish_reason } : {},
210
224
  trace: args.messages
211
225
  };
@@ -389,10 +403,16 @@ function dialogMessageRecordToAgentRuntimeMessage(record) {
389
403
  return {
390
404
  role: record.role,
391
405
  content: record.content ?? "",
406
+ ...record.contextReference !== void 0 ? { context_reference: record.contextReference } : {},
392
407
  ...typeof record.reasoning_content === "string" ? { reasoning_content: record.reasoning_content } : {},
393
408
  ...typeof record.toolCallId === "string" ? { tool_call_id: record.toolCallId } : {},
394
409
  ...Array.isArray(record.tool_calls) ? { tool_calls: record.tool_calls } : {},
395
- ...typeof record.toolName === "string" ? { toolName: record.toolName } : {}
410
+ ...typeof record.toolName === "string" ? { toolName: record.toolName } : {},
411
+ ...(() => {
412
+ const raw = record.createdAt;
413
+ const ms = typeof raw === "number" ? raw : typeof raw === "string" ? Date.parse(raw) : NaN;
414
+ return Number.isFinite(ms) ? { createdAt: ms } : {};
415
+ })()
396
416
  };
397
417
  }
398
418
 
@@ -469,6 +489,7 @@ function buildDialogMessageWriteOps(args) {
469
489
  dialogId: args.dialogId,
470
490
  role: message.role,
471
491
  content: message.content ?? "",
492
+ ...message.context_reference !== void 0 ? { contextReference: message.context_reference } : {},
472
493
  // 持久化思维链(reasoning):空轮/异常排查的关键证据,
473
494
  // 回读由 dialogMessageRecordToAgentRuntimeMessage 还原。
474
495
  ...typeof message.reasoning_content === "string" ? { reasoning_content: message.reasoning_content } : {},
@@ -1338,7 +1359,7 @@ function buildSearchWorkspaceParameters(variant) {
1338
1359
  const path = buildWorkspacePathProperty();
1339
1360
  const includeIgnored = {
1340
1361
  type: "boolean",
1341
- description: "When true, search files ignored by .gitignore such as .tmp. Defaults to false; .git and node_modules remain excluded."
1362
+ 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."
1342
1363
  };
1343
1364
  const exclude = {
1344
1365
  type: "array",
@@ -1701,6 +1722,24 @@ var EXEC_SHELL_TIMEOUT_ENV = "NOLO_EXEC_SHELL_TIMEOUT_MS";
1701
1722
  var EXEC_SHELL_DETACH_ENV = "NOLO_EXEC_SHELL_DETACH_MS";
1702
1723
  var DEFAULT_EXEC_SHELL_DETACH_MS = 12e4;
1703
1724
  var DEFAULT_LOCAL_API_ORIGIN = "http://127.0.0.1:38123";
1725
+ var ALWAYS_EXCLUDED_GLOBS = [
1726
+ "*.tsbuildinfo",
1727
+ "dist/**",
1728
+ "build/**",
1729
+ "out/**",
1730
+ "coverage/**",
1731
+ ".next/**",
1732
+ ".turbo/**",
1733
+ "public/assets/**",
1734
+ "*.min.js",
1735
+ "*.min.css",
1736
+ "*.map",
1737
+ "*.lock",
1738
+ "bun.lockb",
1739
+ "*.ldb"
1740
+ ];
1741
+ var DEFAULT_SEARCH_MAX_RESULTS = 200;
1742
+ var SEARCH_OUTPUT_CHAR_LIMIT = 2e4;
1704
1743
  function readTrimmedString(value) {
1705
1744
  return asOptionalTrimmedString(value);
1706
1745
  }
@@ -2046,8 +2085,29 @@ function sliceReadFileContent(args) {
2046
2085
  truncated: startLine > 1 || effectiveEndLine < totalLines
2047
2086
  };
2048
2087
  }
2049
- function countExactTextOccurrences(args) {
2050
- return args.content.split(args.oldText).length - 1;
2088
+ function buildEolTolerantPattern(oldTextLf) {
2089
+ const escaped = oldTextLf.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2090
+ const pattern = escaped.replace(/\n/g, "\\r?\\n");
2091
+ return new RegExp(pattern, "g");
2092
+ }
2093
+ function countAndReplaceEolTolerant(args) {
2094
+ const oldTextLf = normalizeEolToLf(args.oldText);
2095
+ const newTextLf = normalizeEolToLf(args.newText);
2096
+ const pattern = buildEolTolerantPattern(oldTextLf);
2097
+ let count = 0;
2098
+ const result = args.content.replace(pattern, (match) => {
2099
+ count++;
2100
+ const eol = match.includes("\r\n") ? "\r\n" : "\n";
2101
+ return convertEol(newTextLf, eol);
2102
+ });
2103
+ return { count, result };
2104
+ }
2105
+ function normalizeEolToLf(text) {
2106
+ return text.replace(/\r\n/g, "\n");
2107
+ }
2108
+ function convertEol(text, eol) {
2109
+ const lf = normalizeEolToLf(text);
2110
+ return eol === "\r\n" ? lf.replace(/\n/g, "\r\n") : lf;
2051
2111
  }
2052
2112
  function pluralizeReplacement(count) {
2053
2113
  return count === 1 ? "replacement" : "replacements";
@@ -2068,8 +2128,8 @@ function requireWorkspaceGlobPattern(args) {
2068
2128
  if (!pattern) throw new Error("globFiles requires a non-empty pattern.");
2069
2129
  return pattern;
2070
2130
  }
2071
- function readWorkspaceMaxResults(args) {
2072
- if (args.maxResults === void 0) return void 0;
2131
+ function readWorkspaceMaxResults(args, defaultValue) {
2132
+ if (args.maxResults === void 0) return defaultValue;
2073
2133
  const value = Number(args.maxResults);
2074
2134
  if (!Number.isInteger(value) || value < 1) {
2075
2135
  throw new Error("maxResults must be a positive integer.");
@@ -2245,6 +2305,29 @@ function waitForNodeProcessExit(proc) {
2245
2305
  });
2246
2306
  });
2247
2307
  }
2308
+ function isSpawnFailureError(error) {
2309
+ if (!error || typeof error !== "object") return false;
2310
+ const err = error;
2311
+ if (err.code === "ENOENT") return true;
2312
+ const message = toErrorMessage(error).toLowerCase();
2313
+ return message.includes("enoent") || message.includes("executable not found") || message.includes("spawn") && message.includes("not found");
2314
+ }
2315
+ function spawnFailedCommandResult(error, outputLimit) {
2316
+ const stderr = `spawn failed: ${toErrorMessage(error)}
2317
+ `;
2318
+ return {
2319
+ stdout: "",
2320
+ stderr,
2321
+ exitCode: 127,
2322
+ timedOut: false,
2323
+ spawnFailed: true,
2324
+ content: truncateToolOutput(
2325
+ [`stderr:
2326
+ ${stderr.trim()}`, "exitCode: 127"].filter(Boolean).join("\n\n"),
2327
+ outputLimit
2328
+ )
2329
+ };
2330
+ }
2248
2331
  function resolveRipgrepBinary(env = process.env) {
2249
2332
  const fromEnv = asOptionalTrimmedString(env.NOLO_BUNDLED_RG);
2250
2333
  if (fromEnv) {
@@ -2255,6 +2338,9 @@ function resolveRipgrepBinary(env = process.env) {
2255
2338
  }
2256
2339
  return resolveExecutableOnPath("rg");
2257
2340
  }
2341
+ function resolveGrepBinary() {
2342
+ return resolveExecutableOnPath("grep");
2343
+ }
2258
2344
  async function runWorkspaceCommand(args) {
2259
2345
  const timeoutMs = asOptionalPositiveFiniteNumber(args.timeoutMs);
2260
2346
  const detached = process.platform !== "win32";
@@ -2339,7 +2425,21 @@ async function runWorkspaceCommand(args) {
2339
2425
  }
2340
2426
  if (abortPromise) racers.push(abortPromise);
2341
2427
  if (detachPromise) racers.push(detachPromise);
2342
- const raceWinner = await Promise.race(racers);
2428
+ let raceWinner;
2429
+ try {
2430
+ raceWinner = await Promise.race(racers);
2431
+ } catch (error) {
2432
+ if (timeout) clearTimeout(timeout);
2433
+ if (detachTimer) clearTimeout(detachTimer);
2434
+ if (abortListener && args.abortSignal) {
2435
+ args.abortSignal.removeEventListener("abort", abortListener);
2436
+ }
2437
+ detachSignalCleanup();
2438
+ if (isSpawnFailureError(error)) {
2439
+ return spawnFailedCommandResult(error, args.outputLimit);
2440
+ }
2441
+ throw error;
2442
+ }
2343
2443
  if (timeout) clearTimeout(timeout);
2344
2444
  if (detachTimer) clearTimeout(detachTimer);
2345
2445
  if (abortListener && args.abortSignal) {
@@ -2456,12 +2556,40 @@ async function runWorkspaceCommandLimitedLines(args) {
2456
2556
  ]);
2457
2557
  } catch (error) {
2458
2558
  const settled2 = await exitSettled;
2459
- if (!settled2.ok) throw settled2.error;
2559
+ if (!settled2.ok) {
2560
+ return {
2561
+ stdout: "",
2562
+ stderr: `spawn failed: ${toErrorMessage(settled2.error)}
2563
+ `,
2564
+ exitCode: 127,
2565
+ limitedByMaxResults: false,
2566
+ spawnFailed: true
2567
+ };
2568
+ }
2569
+ if (isSpawnFailureError(error)) {
2570
+ return {
2571
+ stdout: "",
2572
+ stderr: `spawn failed: ${toErrorMessage(error)}
2573
+ `,
2574
+ exitCode: 127,
2575
+ limitedByMaxResults: false,
2576
+ spawnFailed: true
2577
+ };
2578
+ }
2460
2579
  throw error;
2461
2580
  }
2462
2581
  if (!limitedByMaxResults && pending.trim()) lines.push(pending);
2463
2582
  const settled = await exitSettled;
2464
- if (!settled.ok) throw settled.error;
2583
+ if (!settled.ok) {
2584
+ return {
2585
+ stdout: "",
2586
+ stderr: `spawn failed: ${toErrorMessage(settled.error)}
2587
+ `,
2588
+ exitCode: 127,
2589
+ limitedByMaxResults: false,
2590
+ spawnFailed: true
2591
+ };
2592
+ }
2465
2593
  const exitCode = limitedByMaxResults ? 0 : settled.code;
2466
2594
  const stderr = await stderrPromise;
2467
2595
  return {
@@ -2590,13 +2718,16 @@ async function editFileTool(args) {
2590
2718
  ...args.confirmExternalFileAccess ? { confirmExternalFileAccess: args.confirmExternalFileAccess } : {}
2591
2719
  });
2592
2720
  const content = await readFile(absolutePath, "utf8");
2593
- const replacementCount = countExactTextOccurrences({ content, oldText });
2721
+ const { count: replacementCount, result: nextContent } = countAndReplaceEolTolerant({
2722
+ content,
2723
+ oldText,
2724
+ newText
2725
+ });
2594
2726
  if (replacementCount !== expectedReplacements) {
2595
2727
  throw new Error(
2596
- `editFile expected ${expectedReplacements} ${pluralizeReplacement(expectedReplacements)} but found ${replacementCount} in ${requestedPath}.`
2728
+ `editFile expected ${expectedReplacements} ${pluralizeReplacement(expectedReplacements)} but found ${replacementCount} in ${requestedPath} (matched after EOL normalization; verify the exact text).`
2597
2729
  );
2598
2730
  }
2599
- const nextContent = content.split(oldText).join(newText);
2600
2731
  await writeFile(absolutePath, nextContent, "utf8");
2601
2732
  const relativePath = normalizeWorkspaceRelativePath({
2602
2733
  workspaceRoot: resolve(args.workspaceRoot),
@@ -2693,7 +2824,7 @@ async function searchFilesTool(args) {
2693
2824
  const parsed = parseWorkspaceToolArguments(args.call.arguments);
2694
2825
  const query = requireWorkspaceSearchQuery(parsed);
2695
2826
  const exclude = readWorkspaceExcludeGlobs(parsed);
2696
- const maxResults = readWorkspaceMaxResults(parsed);
2827
+ const maxResults = readWorkspaceMaxResults(parsed, DEFAULT_SEARCH_MAX_RESULTS);
2697
2828
  const contextLines = readWorkspaceContextLines(parsed);
2698
2829
  const literal = parsed.literal === true;
2699
2830
  const caseSensitive = parsed.caseSensitive === false ? false : true;
@@ -2708,8 +2839,8 @@ async function searchFilesTool(args) {
2708
2839
  workspaceRoot: resolve(args.workspaceRoot),
2709
2840
  targetPath: searchPath
2710
2841
  });
2711
- const rgBinary = resolveRipgrepBinary();
2712
- const grepBinary = resolveExecutableOnPath("grep") || "grep";
2842
+ const rgBinary = (args.resolveRipgrepBinary ?? resolveRipgrepBinary)();
2843
+ const grepBinary = (args.resolveGrepBinary ?? resolveGrepBinary)();
2713
2844
  const rgCommand = rgBinary ? [
2714
2845
  rgBinary,
2715
2846
  "--line-number",
@@ -2723,29 +2854,26 @@ async function searchFilesTool(args) {
2723
2854
  "!node_modules",
2724
2855
  "--glob",
2725
2856
  "!.git",
2857
+ ...ALWAYS_EXCLUDED_GLOBS.flatMap((g) => ["--glob", `!${g}`]),
2726
2858
  ...exclude.flatMap((excludePattern) => ["--glob", `!${excludePattern}`]),
2727
2859
  "--",
2728
2860
  query,
2729
2861
  relativeSearchPath
2730
2862
  ] : null;
2731
- const grepCommand = [
2732
- grepBinary,
2733
- "-R",
2734
- "-n",
2735
- "-I",
2736
- ...literal ? ["-F"] : [],
2737
- ...caseSensitive ? [] : ["-i"],
2738
- ...contextLines !== void 0 ? ["-C", String(contextLines)] : [],
2739
- "--exclude-dir=node_modules",
2740
- "--exclude-dir=.git",
2741
- ...exclude.flatMap((excludePattern) => ["--exclude", excludePattern]),
2742
- query,
2743
- relativeSearchPath
2863
+ const grepExcludeArgs = [
2864
+ ...ALWAYS_EXCLUDED_GLOBS.flatMap((g) => {
2865
+ if (!g.endsWith("/**")) return ["--exclude", g];
2866
+ const dir = g.replace(/\/\*\*$/, "");
2867
+ return dir.includes("/") ? [] : [`--exclude-dir=${dir}`];
2868
+ }),
2869
+ ...exclude.flatMap((excludePattern) => ["--exclude", excludePattern])
2744
2870
  ];
2871
+ let searchEngine = "js";
2872
+ let binariesUnavailable = !rgBinary && !grepBinary;
2745
2873
  const result = await (async () => {
2746
2874
  if (rgCommand) {
2747
2875
  try {
2748
- return maxResults && !contextLines ? await runWorkspaceCommandLimitedLines({
2876
+ const rgResult = maxResults && !contextLines ? await runWorkspaceCommandLimitedLines({
2749
2877
  workspaceRoot: args.workspaceRoot,
2750
2878
  command: rgCommand,
2751
2879
  maxLines: maxResults
@@ -2753,35 +2881,66 @@ async function searchFilesTool(args) {
2753
2881
  workspaceRoot: args.workspaceRoot,
2754
2882
  command: rgCommand
2755
2883
  });
2884
+ if (!rgResult.spawnFailed) {
2885
+ searchEngine = "ripgrep";
2886
+ return rgResult;
2887
+ }
2756
2888
  } catch {
2757
2889
  }
2758
2890
  }
2759
- try {
2760
- return maxResults && !contextLines ? await runWorkspaceCommandLimitedLines({
2761
- workspaceRoot: args.workspaceRoot,
2762
- command: grepCommand,
2763
- maxLines: maxResults
2764
- }) : await runWorkspaceCommand({
2765
- workspaceRoot: args.workspaceRoot,
2766
- command: grepCommand
2767
- });
2768
- } catch {
2769
- return {
2770
- stdout: "",
2771
- stderr: "",
2772
- exitCode: 0,
2773
- limitedByMaxResults: false
2774
- };
2891
+ if (grepBinary) {
2892
+ const grepCommand = [
2893
+ grepBinary,
2894
+ "-R",
2895
+ "-n",
2896
+ "-I",
2897
+ ...literal ? ["-F"] : [],
2898
+ ...caseSensitive ? [] : ["-i"],
2899
+ ...contextLines !== void 0 ? ["-C", String(contextLines)] : [],
2900
+ "--exclude-dir=node_modules",
2901
+ "--exclude-dir=.git",
2902
+ ...grepExcludeArgs,
2903
+ query,
2904
+ relativeSearchPath
2905
+ ];
2906
+ try {
2907
+ const grepResult = maxResults && !contextLines ? await runWorkspaceCommandLimitedLines({
2908
+ workspaceRoot: args.workspaceRoot,
2909
+ command: grepCommand,
2910
+ maxLines: maxResults
2911
+ }) : await runWorkspaceCommand({
2912
+ workspaceRoot: args.workspaceRoot,
2913
+ command: grepCommand
2914
+ });
2915
+ if (!grepResult.spawnFailed) {
2916
+ searchEngine = "grep";
2917
+ return grepResult;
2918
+ }
2919
+ binariesUnavailable = true;
2920
+ } catch {
2921
+ binariesUnavailable = true;
2922
+ }
2923
+ } else {
2924
+ binariesUnavailable = true;
2775
2925
  }
2926
+ return {
2927
+ stdout: "",
2928
+ stderr: "",
2929
+ exitCode: 0,
2930
+ limitedByMaxResults: false,
2931
+ spawnFailed: true
2932
+ };
2776
2933
  })();
2777
2934
  let outputLines = result.stdout.split(/\r?\n/).map((line) => line.trimEnd().replace(/^\.\//, "")).filter(Boolean);
2935
+ outputLines = dropAlwaysExcludedLines(outputLines);
2778
2936
  let limitedByMaxResults = "limitedByMaxResults" in result && result.limitedByMaxResults === true;
2779
- if (outputLines.length === 0 && result.exitCode === 0 && (includeIgnored || !await hasRootGitignore(args.workspaceRoot))) {
2937
+ const forceJsFallback = binariesUnavailable || result.spawnFailed === true;
2938
+ if (outputLines.length === 0 && (forceJsFallback || result.exitCode === 0 && (includeIgnored || !await hasRootGitignore(args.workspaceRoot)))) {
2780
2939
  const fallback = await scanWorkspaceTextMatches({
2781
2940
  workspaceRoot: args.workspaceRoot,
2782
2941
  relativeSearchPath,
2783
2942
  query,
2784
- exclude,
2943
+ exclude: [...ALWAYS_EXCLUDED_GLOBS, ...exclude],
2785
2944
  maxResults,
2786
2945
  literal,
2787
2946
  caseSensitive,
@@ -2789,6 +2948,7 @@ async function searchFilesTool(args) {
2789
2948
  });
2790
2949
  outputLines = fallback.lines;
2791
2950
  limitedByMaxResults = fallback.limitedByMaxResults;
2951
+ searchEngine = "js";
2792
2952
  }
2793
2953
  if (contextLines && outputLines.length > 0) {
2794
2954
  const limited = limitSearchOutputByMatches(outputLines, maxResults);
@@ -2800,8 +2960,11 @@ async function searchFilesTool(args) {
2800
2960
  const match = line.match(/^(.*?):\d+:/);
2801
2961
  return match?.[1] ? [match[1]] : [];
2802
2962
  })));
2963
+ const rawContent = outputLines.join("\n");
2964
+ const content = truncateToolOutput(rawContent, SEARCH_OUTPUT_CHAR_LIMIT);
2965
+ const truncatedByByteLimit = content.length !== rawContent.length;
2803
2966
  return {
2804
- content: outputLines.join("\n"),
2967
+ content,
2805
2968
  metadata: {
2806
2969
  query,
2807
2970
  path: requestedPath,
@@ -2814,10 +2977,12 @@ async function searchFilesTool(args) {
2814
2977
  count: outputLines.length,
2815
2978
  matchCount: matchLines.length,
2816
2979
  matchedFiles,
2817
- truncated: limitedByMaxResults,
2980
+ truncated: limitedByMaxResults || truncatedByByteLimit,
2818
2981
  limitedByMaxResults,
2982
+ ...truncatedByByteLimit ? { truncatedByByteLimit: true } : {},
2819
2983
  ...maxResults ? { maxResults } : {},
2820
2984
  exitCode: result.exitCode,
2985
+ searchEngine,
2821
2986
  ...extractActivity(parsed) ? { activity: extractActivity(parsed) } : {}
2822
2987
  }
2823
2988
  };
@@ -2848,6 +3013,14 @@ async function filterRootGitignoredFiles(args) {
2848
3013
  const globs = patterns.map((pattern) => createGlob(pattern));
2849
3014
  return args.files.filter((file) => !globs.some((glob) => glob.match(file)));
2850
3015
  }
3016
+ function dropAlwaysExcludedLines(lines) {
3017
+ const globs = ALWAYS_EXCLUDED_GLOBS.map((pattern) => createGlob(pattern));
3018
+ return lines.filter((line) => {
3019
+ const filePath = line.match(/^(.*?):\d+[:-]/)?.[1];
3020
+ if (!filePath) return true;
3021
+ return !globs.some((glob) => glob.match(filePath));
3022
+ });
3023
+ }
2851
3024
  function scanWorkspaceGlobFiles(args) {
2852
3025
  const pathPrefix = args.relativeSearchPath === "." ? "" : `${args.relativeSearchPath.replace(/\/+$/, "")}/`;
2853
3026
  return Array.from(createGlob(args.pattern).scanSync({
@@ -2940,38 +3113,76 @@ async function globFilesTool(args) {
2940
3113
  workspaceRoot: resolve(args.workspaceRoot),
2941
3114
  targetPath: searchPath
2942
3115
  });
2943
- const command = [
2944
- "rg",
2945
- "--files",
2946
- "--hidden",
2947
- ...includeIgnored ? ["--no-ignore"] : [],
2948
- "--glob",
2949
- pattern,
2950
- "--glob",
2951
- "!node_modules",
2952
- "--glob",
2953
- "!.git",
2954
- ...exclude.flatMap((excludePattern) => ["--glob", `!${excludePattern}`]),
2955
- ...relativeSearchPath === "." ? [] : [relativeSearchPath]
2956
- ];
2957
- const result = await runWorkspaceCommand({
2958
- workspaceRoot: args.workspaceRoot,
2959
- command
2960
- });
2961
3116
  const pathPrefix = relativeSearchPath === "." ? "" : `${relativeSearchPath.replace(/\/+$/, "")}/`;
2962
- 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));
2963
- files = await filterRootGitignoredFiles({
2964
- workspaceRoot: args.workspaceRoot,
2965
- files,
2966
- includeIgnored
2967
- });
2968
- if (files.length === 0 && (includeIgnored || !await hasRootGitignore(args.workspaceRoot))) {
3117
+ const rgBinary = (args.resolveRipgrepBinary ?? resolveRipgrepBinary)();
3118
+ let searchEngine = "js";
3119
+ let exitCode = 0;
3120
+ let files = [];
3121
+ if (rgBinary) {
3122
+ const command = [
3123
+ rgBinary,
3124
+ "--files",
3125
+ "--hidden",
3126
+ ...includeIgnored ? ["--no-ignore"] : [],
3127
+ "--glob",
3128
+ pattern,
3129
+ "--glob",
3130
+ "!node_modules",
3131
+ "--glob",
3132
+ "!.git",
3133
+ ...exclude.flatMap((excludePattern) => ["--glob", `!${excludePattern}`]),
3134
+ ...relativeSearchPath === "." ? [] : [relativeSearchPath]
3135
+ ];
3136
+ const result = await runWorkspaceCommand({
3137
+ workspaceRoot: args.workspaceRoot,
3138
+ command
3139
+ });
3140
+ exitCode = result.exitCode;
3141
+ if (result.spawnFailed) {
3142
+ files = scanWorkspaceGlobFiles({
3143
+ workspaceRoot: args.workspaceRoot,
3144
+ pattern,
3145
+ relativeSearchPath,
3146
+ exclude
3147
+ });
3148
+ files = await filterRootGitignoredFiles({
3149
+ workspaceRoot: args.workspaceRoot,
3150
+ files,
3151
+ includeIgnored
3152
+ });
3153
+ searchEngine = "js";
3154
+ } else {
3155
+ 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));
3156
+ files = await filterRootGitignoredFiles({
3157
+ workspaceRoot: args.workspaceRoot,
3158
+ files,
3159
+ includeIgnored
3160
+ });
3161
+ if (files.length === 0 && (includeIgnored || !await hasRootGitignore(args.workspaceRoot))) {
3162
+ files = scanWorkspaceGlobFiles({
3163
+ workspaceRoot: args.workspaceRoot,
3164
+ pattern,
3165
+ relativeSearchPath,
3166
+ exclude
3167
+ });
3168
+ searchEngine = "js";
3169
+ } else {
3170
+ searchEngine = "ripgrep";
3171
+ }
3172
+ }
3173
+ } else {
2969
3174
  files = scanWorkspaceGlobFiles({
2970
3175
  workspaceRoot: args.workspaceRoot,
2971
3176
  pattern,
2972
3177
  relativeSearchPath,
2973
3178
  exclude
2974
3179
  });
3180
+ files = await filterRootGitignoredFiles({
3181
+ workspaceRoot: args.workspaceRoot,
3182
+ files,
3183
+ includeIgnored
3184
+ });
3185
+ searchEngine = "js";
2975
3186
  }
2976
3187
  const commandLimitedByMaxResults = false;
2977
3188
  const totalCount = commandLimitedByMaxResults ? void 0 : files.length;
@@ -2992,7 +3203,8 @@ async function globFilesTool(args) {
2992
3203
  truncated: limitedByMaxResults,
2993
3204
  limitedByMaxResults,
2994
3205
  ...maxResults ? { maxResults } : {},
2995
- exitCode: result.exitCode,
3206
+ exitCode,
3207
+ searchEngine,
2996
3208
  ...activity ? { activity } : {}
2997
3209
  }
2998
3210
  };
@@ -3196,6 +3408,10 @@ async function listProcessesTool(args) {
3196
3408
  }
3197
3409
  function createLocalWorkspaceToolExecutors(args) {
3198
3410
  const fileAccess = args.confirmExternalFileAccess ? { confirmExternalFileAccess: args.confirmExternalFileAccess } : {};
3411
+ const searchBinaryResolvers = {
3412
+ ...args.resolveRipgrepBinary ? { resolveRipgrepBinary: args.resolveRipgrepBinary } : {},
3413
+ ...args.resolveGrepBinary ? { resolveGrepBinary: args.resolveGrepBinary } : {}
3414
+ };
3199
3415
  return {
3200
3416
  editFile: (call) => editFileTool({
3201
3417
  call,
@@ -3205,7 +3421,8 @@ function createLocalWorkspaceToolExecutors(args) {
3205
3421
  globFiles: (call) => globFilesTool({
3206
3422
  call,
3207
3423
  workspaceRoot: args.workspaceRoot,
3208
- ...fileAccess
3424
+ ...fileAccess,
3425
+ ...args.resolveRipgrepBinary ? { resolveRipgrepBinary: args.resolveRipgrepBinary } : {}
3209
3426
  }),
3210
3427
  listFiles: (call) => listFilesTool({
3211
3428
  call,
@@ -3225,7 +3442,8 @@ function createLocalWorkspaceToolExecutors(args) {
3225
3442
  searchFiles: (call) => searchFilesTool({
3226
3443
  call,
3227
3444
  workspaceRoot: args.workspaceRoot,
3228
- ...fileAccess
3445
+ ...fileAccess,
3446
+ ...searchBinaryResolvers
3229
3447
  }),
3230
3448
  captureVisualState: (call) => captureVisualStateTool({
3231
3449
  call,