@morphllm/morphsdk 0.2.57 → 0.2.59

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 (170) hide show
  1. package/dist/anthropic-CaFUHxBW.d.ts +89 -0
  2. package/dist/{chunk-6X5UOY7B.js → chunk-2CASO3ZO.js} +46 -79
  3. package/dist/chunk-2CASO3ZO.js.map +1 -0
  4. package/dist/chunk-374N3GIA.js +118 -0
  5. package/dist/chunk-374N3GIA.js.map +1 -0
  6. package/dist/chunk-3IQIT6MC.js +65 -0
  7. package/dist/chunk-3IQIT6MC.js.map +1 -0
  8. package/dist/chunk-4VGOBA2J.js +57 -0
  9. package/dist/chunk-4VGOBA2J.js.map +1 -0
  10. package/dist/chunk-527P5X2E.js +98 -0
  11. package/dist/chunk-527P5X2E.js.map +1 -0
  12. package/dist/chunk-6N6ZYZYD.js +74 -0
  13. package/dist/chunk-6N6ZYZYD.js.map +1 -0
  14. package/dist/chunk-6Y5JB4JC.js +195 -0
  15. package/dist/chunk-6Y5JB4JC.js.map +1 -0
  16. package/dist/{chunk-TICMYDII.js → chunk-APP75CBN.js} +33 -16
  17. package/dist/chunk-APP75CBN.js.map +1 -0
  18. package/dist/{chunk-QFIHUCTF.js → chunk-FN4EP3WY.js} +19 -19
  19. package/dist/chunk-FN4EP3WY.js.map +1 -0
  20. package/dist/chunk-ILJ3J5IA.js +72 -0
  21. package/dist/chunk-ILJ3J5IA.js.map +1 -0
  22. package/dist/chunk-ISWL67SF.js +1 -0
  23. package/dist/chunk-KW7OEGZK.js +9 -0
  24. package/dist/chunk-KW7OEGZK.js.map +1 -0
  25. package/dist/chunk-Q5AHGIQO.js +205 -0
  26. package/dist/chunk-Q5AHGIQO.js.map +1 -0
  27. package/dist/{chunk-OXHGFHEU.js → chunk-VJU3BRET.js} +3 -3
  28. package/dist/chunk-VJU3BRET.js.map +1 -0
  29. package/dist/{chunk-TJIUA27P.js → chunk-XT5ZO6ES.js} +9 -5
  30. package/dist/chunk-XT5ZO6ES.js.map +1 -0
  31. package/dist/{chunk-LVPVVLTI.js → chunk-YV75OQTE.js} +105 -17
  32. package/dist/chunk-YV75OQTE.js.map +1 -0
  33. package/dist/{chunk-ZJIIICRA.js → chunk-ZO4PPFCZ.js} +60 -29
  34. package/dist/chunk-ZO4PPFCZ.js.map +1 -0
  35. package/dist/{client-CFoR--IU.d.ts → client-CextMMm9.d.ts} +10 -15
  36. package/dist/client.cjs +689 -343
  37. package/dist/client.cjs.map +1 -1
  38. package/dist/client.d.ts +3 -2
  39. package/dist/client.js +15 -15
  40. package/dist/finish-kXAcUJyB.d.ts +33 -0
  41. package/dist/gemini-CE80Pbdy.d.ts +117 -0
  42. package/dist/git/client.cjs +2 -2
  43. package/dist/git/client.cjs.map +1 -1
  44. package/dist/git/client.d.ts +1 -1
  45. package/dist/git/client.js +1 -1
  46. package/dist/git/index.cjs +2 -2
  47. package/dist/git/index.cjs.map +1 -1
  48. package/dist/git/index.js +1 -1
  49. package/dist/git/types.cjs.map +1 -1
  50. package/dist/git/types.d.ts +1 -1
  51. package/dist/index.cjs +702 -343
  52. package/dist/index.cjs.map +1 -1
  53. package/dist/index.d.ts +4 -3
  54. package/dist/index.js +17 -16
  55. package/dist/openai-Fvpqln7F.d.ts +89 -0
  56. package/dist/tools/warp_grep/agent/config.cjs +8 -4
  57. package/dist/tools/warp_grep/agent/config.cjs.map +1 -1
  58. package/dist/tools/warp_grep/agent/config.d.ts +7 -2
  59. package/dist/tools/warp_grep/agent/config.js +1 -1
  60. package/dist/tools/warp_grep/agent/formatter.cjs +32 -15
  61. package/dist/tools/warp_grep/agent/formatter.cjs.map +1 -1
  62. package/dist/tools/warp_grep/agent/formatter.d.ts +1 -1
  63. package/dist/tools/warp_grep/agent/formatter.js +1 -1
  64. package/dist/tools/warp_grep/agent/parser.cjs +104 -17
  65. package/dist/tools/warp_grep/agent/parser.cjs.map +1 -1
  66. package/dist/tools/warp_grep/agent/parser.d.ts +3 -5
  67. package/dist/tools/warp_grep/agent/parser.js +1 -3
  68. package/dist/tools/warp_grep/agent/prompt.cjs +132 -56
  69. package/dist/tools/warp_grep/agent/prompt.cjs.map +1 -1
  70. package/dist/tools/warp_grep/agent/prompt.d.ts +1 -1
  71. package/dist/tools/warp_grep/agent/prompt.js +1 -1
  72. package/dist/tools/warp_grep/agent/runner.cjs +459 -192
  73. package/dist/tools/warp_grep/agent/runner.cjs.map +1 -1
  74. package/dist/tools/warp_grep/agent/runner.d.ts +1 -0
  75. package/dist/tools/warp_grep/agent/runner.js +6 -8
  76. package/dist/tools/warp_grep/agent/types.cjs.map +1 -1
  77. package/dist/tools/warp_grep/agent/types.d.ts +9 -2
  78. package/dist/tools/warp_grep/anthropic.cjs +650 -260
  79. package/dist/tools/warp_grep/anthropic.cjs.map +1 -1
  80. package/dist/tools/warp_grep/anthropic.d.ts +4 -74
  81. package/dist/tools/warp_grep/anthropic.js +13 -15
  82. package/dist/tools/warp_grep/client.cjs +1593 -0
  83. package/dist/tools/warp_grep/client.cjs.map +1 -0
  84. package/dist/tools/warp_grep/client.d.ts +87 -0
  85. package/dist/tools/warp_grep/client.js +26 -0
  86. package/dist/tools/warp_grep/gemini.cjs +1587 -0
  87. package/dist/tools/warp_grep/gemini.cjs.map +1 -0
  88. package/dist/tools/warp_grep/gemini.d.ts +7 -0
  89. package/dist/tools/warp_grep/gemini.js +34 -0
  90. package/dist/tools/warp_grep/harness.cjs +556 -220
  91. package/dist/tools/warp_grep/harness.cjs.map +1 -1
  92. package/dist/tools/warp_grep/harness.d.ts +50 -119
  93. package/dist/tools/warp_grep/harness.js +33 -41
  94. package/dist/tools/warp_grep/harness.js.map +1 -1
  95. package/dist/tools/warp_grep/index.cjs +812 -346
  96. package/dist/tools/warp_grep/index.cjs.map +1 -1
  97. package/dist/tools/warp_grep/index.d.ts +11 -6
  98. package/dist/tools/warp_grep/index.js +43 -22
  99. package/dist/tools/warp_grep/openai.cjs +650 -258
  100. package/dist/tools/warp_grep/openai.cjs.map +1 -1
  101. package/dist/tools/warp_grep/openai.d.ts +4 -74
  102. package/dist/tools/warp_grep/openai.js +13 -13
  103. package/dist/tools/warp_grep/providers/local.cjs +66 -27
  104. package/dist/tools/warp_grep/providers/local.cjs.map +1 -1
  105. package/dist/tools/warp_grep/providers/local.d.ts +4 -9
  106. package/dist/tools/warp_grep/providers/local.js +2 -2
  107. package/dist/tools/warp_grep/providers/remote.cjs +211 -0
  108. package/dist/tools/warp_grep/providers/remote.cjs.map +1 -0
  109. package/dist/tools/warp_grep/providers/remote.d.ts +67 -0
  110. package/dist/tools/warp_grep/providers/remote.js +9 -0
  111. package/dist/tools/warp_grep/providers/types.cjs.map +1 -1
  112. package/dist/tools/warp_grep/providers/types.d.ts +7 -15
  113. package/dist/tools/warp_grep/vercel.cjs +662 -277
  114. package/dist/tools/warp_grep/vercel.cjs.map +1 -1
  115. package/dist/tools/warp_grep/vercel.d.ts +4 -51
  116. package/dist/tools/warp_grep/vercel.js +16 -14
  117. package/dist/types-a_hxdPI6.d.ts +144 -0
  118. package/dist/vercel-3yjvfmVB.d.ts +66 -0
  119. package/package.json +12 -2
  120. package/dist/chunk-6X5UOY7B.js.map +0 -1
  121. package/dist/chunk-73RQWOQC.js +0 -16
  122. package/dist/chunk-73RQWOQC.js.map +0 -1
  123. package/dist/chunk-7OQOOB3R.js +0 -1
  124. package/dist/chunk-CFF636UC.js +0 -70
  125. package/dist/chunk-CFF636UC.js.map +0 -1
  126. package/dist/chunk-EK7OQPWD.js +0 -44
  127. package/dist/chunk-EK7OQPWD.js.map +0 -1
  128. package/dist/chunk-GJ5TYNRD.js +0 -107
  129. package/dist/chunk-GJ5TYNRD.js.map +0 -1
  130. package/dist/chunk-HQO45BAJ.js +0 -14
  131. package/dist/chunk-HQO45BAJ.js.map +0 -1
  132. package/dist/chunk-IMYQOKFO.js +0 -83
  133. package/dist/chunk-IMYQOKFO.js.map +0 -1
  134. package/dist/chunk-KBQWGT5L.js +0 -77
  135. package/dist/chunk-KBQWGT5L.js.map +0 -1
  136. package/dist/chunk-LVPVVLTI.js.map +0 -1
  137. package/dist/chunk-OXHGFHEU.js.map +0 -1
  138. package/dist/chunk-QFIHUCTF.js.map +0 -1
  139. package/dist/chunk-TICMYDII.js.map +0 -1
  140. package/dist/chunk-TJIUA27P.js.map +0 -1
  141. package/dist/chunk-WETRQJGU.js +0 -129
  142. package/dist/chunk-WETRQJGU.js.map +0 -1
  143. package/dist/chunk-ZJIIICRA.js.map +0 -1
  144. package/dist/core-CpkYEi_T.d.ts +0 -158
  145. package/dist/tools/warp_grep/tools/analyse.cjs +0 -40
  146. package/dist/tools/warp_grep/tools/analyse.cjs.map +0 -1
  147. package/dist/tools/warp_grep/tools/analyse.d.ts +0 -10
  148. package/dist/tools/warp_grep/tools/analyse.js +0 -8
  149. package/dist/tools/warp_grep/tools/finish.cjs +0 -69
  150. package/dist/tools/warp_grep/tools/finish.cjs.map +0 -1
  151. package/dist/tools/warp_grep/tools/finish.d.ts +0 -10
  152. package/dist/tools/warp_grep/tools/finish.js +0 -10
  153. package/dist/tools/warp_grep/tools/grep.cjs +0 -38
  154. package/dist/tools/warp_grep/tools/grep.cjs.map +0 -1
  155. package/dist/tools/warp_grep/tools/grep.d.ts +0 -8
  156. package/dist/tools/warp_grep/tools/grep.js +0 -15
  157. package/dist/tools/warp_grep/tools/grep.js.map +0 -1
  158. package/dist/tools/warp_grep/tools/read.cjs +0 -38
  159. package/dist/tools/warp_grep/tools/read.cjs.map +0 -1
  160. package/dist/tools/warp_grep/tools/read.d.ts +0 -9
  161. package/dist/tools/warp_grep/tools/read.js +0 -8
  162. package/dist/tools/warp_grep/utils/format.cjs +0 -42
  163. package/dist/tools/warp_grep/utils/format.cjs.map +0 -1
  164. package/dist/tools/warp_grep/utils/format.d.ts +0 -4
  165. package/dist/tools/warp_grep/utils/format.js +0 -18
  166. package/dist/tools/warp_grep/utils/format.js.map +0 -1
  167. /package/dist/{chunk-7OQOOB3R.js.map → chunk-ISWL67SF.js.map} +0 -0
  168. /package/dist/tools/warp_grep/{tools/analyse.js.map → client.js.map} +0 -0
  169. /package/dist/tools/warp_grep/{tools/finish.js.map → gemini.js.map} +0 -0
  170. /package/dist/tools/warp_grep/{tools/read.js.map → providers/remote.js.map} +0 -0
@@ -30,23 +30,127 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // tools/warp_grep/harness.ts
31
31
  var harness_exports = {};
32
32
  __export(harness_exports, {
33
+ AGENT_CONFIG: () => AGENT_CONFIG,
33
34
  DEFAULT_EXCLUDES: () => DEFAULT_EXCLUDES,
35
+ LLMResponseParser: () => LLMResponseParser,
34
36
  LocalRipgrepProvider: () => LocalRipgrepProvider,
35
- MAX_TURNS: () => MAX_TURNS,
36
37
  SYSTEM_PROMPT: () => SYSTEM_PROMPT,
37
- TIMEOUT_MS: () => TIMEOUT_MS,
38
- formatAnalyseTree: () => formatAnalyseTree,
39
- formatToolResult: () => formatToolResult,
38
+ buildInitialState: () => buildInitialState,
39
+ calculateContextBudget: () => calculateContextBudget,
40
+ formatListDirectoryTree: () => formatListDirectoryTree,
41
+ formatToolResult: () => formatAgentToolOutput,
40
42
  formatTurnMessage: () => formatTurnMessage,
43
+ getSystemPrompt: () => getSystemPrompt,
44
+ normalizeFinishFiles: () => normalizeFinishFiles,
41
45
  parseToolCalls: () => parseToolCalls,
42
- resolveFinishFiles: () => resolveFinishFiles
46
+ readFinishFiles: () => readFinishFiles,
47
+ resolveFinishFiles: () => resolveFinishFiles,
48
+ toolGrep: () => toolGrep,
49
+ toolListDirectory: () => toolListDirectory,
50
+ toolRead: () => toolRead
43
51
  });
44
52
  module.exports = __toCommonJS(harness_exports);
45
53
 
46
54
  // tools/warp_grep/agent/parser.ts
47
- var VALID_COMMANDS = ["analyse", "grep", "read", "finish"];
55
+ var VALID_COMMANDS = ["list_directory", "grep", "read", "finish"];
56
+ function isValidCommand(name) {
57
+ return VALID_COMMANDS.includes(name);
58
+ }
59
+ function getXmlElementText(xml, tagName) {
60
+ const regex = new RegExp(`<${tagName}>([\\s\\S]*?)</${tagName}>`, "i");
61
+ const match = xml.match(regex);
62
+ return match ? match[1].trim() : null;
63
+ }
64
+ function parseNestedXmlTools(text) {
65
+ const tools = [];
66
+ const toolRegex = /<([a-z_][a-z0-9_]*)>([\s\S]*?)<\/\1>/gi;
67
+ let match;
68
+ while ((match = toolRegex.exec(text)) !== null) {
69
+ const rawToolName = match[1].toLowerCase();
70
+ const content = match[2];
71
+ if (!isValidCommand(rawToolName)) continue;
72
+ const toolName = rawToolName;
73
+ if (toolName === "list_directory") {
74
+ const path4 = getXmlElementText(content, "path");
75
+ const pattern = getXmlElementText(content, "pattern");
76
+ if (path4) {
77
+ tools.push({ name: "list_directory", arguments: { path: path4, pattern } });
78
+ }
79
+ } else if (toolName === "grep") {
80
+ const pattern = getXmlElementText(content, "pattern");
81
+ const subDir = getXmlElementText(content, "sub_dir");
82
+ const glob = getXmlElementText(content, "glob");
83
+ if (pattern) {
84
+ tools.push({
85
+ name: "grep",
86
+ arguments: {
87
+ pattern,
88
+ path: subDir || ".",
89
+ ...glob && { glob }
90
+ }
91
+ });
92
+ }
93
+ } else if (toolName === "read") {
94
+ const path4 = getXmlElementText(content, "path");
95
+ const linesStr = getXmlElementText(content, "lines");
96
+ if (path4) {
97
+ const args = { path: path4 };
98
+ if (linesStr) {
99
+ const ranges = [];
100
+ for (const rangeStr of linesStr.split(",")) {
101
+ const trimmed = rangeStr.trim();
102
+ if (!trimmed) continue;
103
+ const [s, e] = trimmed.split("-").map((v) => parseInt(v.trim(), 10));
104
+ if (Number.isFinite(s) && Number.isFinite(e)) {
105
+ ranges.push([s, e]);
106
+ } else if (Number.isFinite(s)) {
107
+ ranges.push([s, s]);
108
+ }
109
+ }
110
+ if (ranges.length === 1) {
111
+ args.start = ranges[0][0];
112
+ args.end = ranges[0][1];
113
+ } else if (ranges.length > 1) {
114
+ args.lines = ranges;
115
+ }
116
+ }
117
+ tools.push({ name: "read", arguments: args });
118
+ }
119
+ } else if (toolName === "finish") {
120
+ const fileRegex = /<file>([\s\S]*?)<\/file>/gi;
121
+ const files = [];
122
+ let fileMatch;
123
+ while ((fileMatch = fileRegex.exec(content)) !== null) {
124
+ const fileContent = fileMatch[1];
125
+ const filePath = getXmlElementText(fileContent, "path");
126
+ const linesStr = getXmlElementText(fileContent, "lines");
127
+ if (filePath && linesStr) {
128
+ const ranges = [];
129
+ for (const rangeStr of linesStr.split(",")) {
130
+ if (rangeStr.trim() === "*") {
131
+ ranges.push([1, 999999]);
132
+ } else {
133
+ const [s, e] = rangeStr.split("-").map((v) => parseInt(v.trim(), 10));
134
+ if (Number.isFinite(s) && Number.isFinite(e)) {
135
+ ranges.push([s, e]);
136
+ }
137
+ }
138
+ }
139
+ if (ranges.length > 0) {
140
+ files.push({ path: filePath, lines: ranges });
141
+ }
142
+ }
143
+ }
144
+ if (files.length > 0) {
145
+ tools.push({ name: "finish", arguments: { files } });
146
+ }
147
+ }
148
+ }
149
+ return tools;
150
+ }
48
151
  function preprocessText(text) {
49
152
  let processed = text.replace(/<think>[\s\S]*?<\/think>/gi, "");
153
+ const nestedTools = parseNestedXmlTools(processed);
50
154
  const openingTagRegex = /<tool_call>|<tool>/gi;
51
155
  const closingTagRegex = /<\/tool_call>|<\/tool>/gi;
52
156
  const openingMatches = processed.match(openingTagRegex) || [];
@@ -83,7 +187,7 @@ function preprocessText(text) {
83
187
  }
84
188
  }
85
189
  }
86
- return toolCallLines;
190
+ return { lines: toolCallLines, nestedTools };
87
191
  }
88
192
  var LLMResponseParser = class {
89
193
  finishSpecSplitRe = /,(?=[^,\s]+:)/;
@@ -91,8 +195,8 @@ var LLMResponseParser = class {
91
195
  if (typeof text !== "string") {
92
196
  throw new TypeError("Command text must be a string.");
93
197
  }
94
- const lines = preprocessText(text);
95
- const commands = [];
198
+ const { lines, nestedTools } = preprocessText(text);
199
+ const commands = [...nestedTools];
96
200
  let finishAccumulator = null;
97
201
  lines.forEach((line) => {
98
202
  if (!line || line.startsWith("#")) return;
@@ -100,8 +204,8 @@ var LLMResponseParser = class {
100
204
  if (parts.length === 0) return;
101
205
  const cmd = parts[0];
102
206
  switch (cmd) {
103
- case "analyse":
104
- this.handleAnalyse(parts, line, commands);
207
+ case "list_directory":
208
+ this.handleListDirectory(parts, line, commands);
105
209
  break;
106
210
  case "grep":
107
211
  this.handleGrep(parts, line, commands);
@@ -119,8 +223,8 @@ var LLMResponseParser = class {
119
223
  if (finishAccumulator) {
120
224
  const map = finishAccumulator;
121
225
  const entries = [...map.entries()];
122
- const filesPayload = entries.map(([path3, ranges]) => ({
123
- path: path3,
226
+ const filesPayload = entries.map(([path4, ranges]) => ({
227
+ path: path4,
124
228
  lines: [...ranges].sort((a, b) => a[0] - b[0])
125
229
  }));
126
230
  commands.push({ name: "finish", arguments: { files: filesPayload } });
@@ -152,18 +256,17 @@ var LLMResponseParser = class {
152
256
  skip(message) {
153
257
  return { name: "_skip", arguments: { message } };
154
258
  }
155
- handleAnalyse(parts, rawLine, commands) {
259
+ handleListDirectory(parts, rawLine, commands) {
156
260
  if (parts.length < 2) {
157
261
  commands.push(this.skip(
158
- `[SKIPPED] Your command "${rawLine}" is missing a path. Correct format: analyse <path> [pattern]. Example: analyse src/`
262
+ `[SKIPPED] Your command "${rawLine}" is missing a path. Correct format: list_directory <path> [pattern]. Example: list_directory src/`
159
263
  ));
160
264
  return;
161
265
  }
162
- const path3 = parts[1];
266
+ const path4 = parts[1];
163
267
  const pattern = parts[2]?.replace(/^"|"$/g, "") ?? null;
164
- commands.push({ name: "analyse", arguments: { path: path3, pattern } });
268
+ commands.push({ name: "list_directory", arguments: { path: path4, pattern } });
165
269
  }
166
- // no glob tool in MCP
167
270
  handleGrep(parts, rawLine, commands) {
168
271
  if (parts.length < 3) {
169
272
  commands.push(this.skip(
@@ -234,187 +337,189 @@ var LLMResponseParser = class {
234
337
  }
235
338
  };
236
339
 
237
- // tools/warp_grep/agent/formatter.ts
238
- var ToolOutputFormatter = class {
239
- format(toolName, args, output, options = {}) {
240
- const name = (toolName ?? "").trim();
241
- if (!name) {
242
- return "";
243
- }
244
- const payload = output?.toString?.()?.trim?.() ?? "";
245
- const isError = Boolean(options.isError);
246
- const safeArgs = args ?? {};
247
- if (!payload && !isError) {
248
- return "";
249
- }
250
- switch (name) {
251
- case "read":
252
- return this.formatRead(safeArgs, payload, isError);
253
- case "analyse":
254
- return this.formatAnalyse(safeArgs, payload, isError);
255
- case "grep":
256
- return this.formatGrep(safeArgs, payload, isError);
257
- default:
258
- return payload ? `<tool_output>
259
- ${payload}
260
- </tool_output>` : "";
261
- }
262
- }
263
- formatRead(args, payload, isError) {
264
- if (isError) {
265
- return payload;
266
- }
267
- const path3 = this.asString(args.path) || "...";
268
- return `<file path="${path3}">
269
- ${payload}
270
- </file>`;
271
- }
272
- formatAnalyse(args, payload, isError) {
273
- const path3 = this.asString(args.path) || ".";
274
- if (isError) {
275
- return `<analyse_results path="${path3}" status="error">
276
- ${payload}
277
- </analyse_results>`;
278
- }
279
- return `<analyse_results path="${path3}">
280
- ${payload}
281
- </analyse_results>`;
282
- }
283
- formatGrep(args, payload, isError) {
284
- const pattern = this.asString(args.pattern);
285
- const path3 = this.asString(args.path);
286
- const attributes = [];
287
- if (pattern !== void 0) {
288
- attributes.push(`pattern="${pattern}"`);
289
- }
290
- if (path3 !== void 0) {
291
- attributes.push(`path="${path3}"`);
292
- }
293
- if (isError) {
294
- attributes.push('status="error"');
295
- }
296
- const attrText = attributes.length ? ` ${attributes.join(" ")}` : "";
297
- return `<grep_output${attrText}>
298
- ${payload}
299
- </grep_output>`;
300
- }
301
- asString(value) {
302
- if (value === null || value === void 0) {
303
- return void 0;
304
- }
305
- return String(value);
306
- }
307
- };
308
- var sharedFormatter = new ToolOutputFormatter();
309
- function formatAgentToolOutput(toolName, args, output, options = {}) {
310
- return sharedFormatter.format(toolName, args, output, options);
311
- }
312
-
313
340
  // tools/warp_grep/agent/prompt.ts
314
- var SYSTEM_PROMPT = `You are a code search agent. Your task is to find all relevant code for a given query.
341
+ var SYSTEM_PROMPT = `You are a code search agent. Your task is to find all relevant code for a given search_string.
315
342
 
316
- <workflow>
343
+ ### workflow
317
344
  You have exactly 4 turns. The 4th turn MUST be a \`finish\` call. Each turn allows up to 8 parallel tool calls.
318
345
 
319
- - Turn 1: Map the territory OR dive deep (based on query specificity)
346
+ - Turn 1: Map the territory OR dive deep (based on search_string specificity)
320
347
  - Turn 2-3: Refine based on findings
321
348
  - Turn 4: MUST call \`finish\` with all relevant code locations
322
349
  - You MAY call \`finish\` early if confident\u2014but never before at least 1 search turn.
350
+ - The user strongly prefers if you can call the finish tool early, but you must be correct
323
351
 
324
- Remember, if the task feels easy to you, it is strongly desirable to call \`finish\` early using fewer turns, but quality over speed.
325
- </workflow>
352
+ Remember, if the task feels easy to you, it is strongly desirable to call 'finish' early using fewer turns, but quality over speed
326
353
 
327
- <tools>
328
- ### \`analyse <path> [pattern]\`
329
- Directory tree or file search. Shows structure of a path, optionally filtered by regex pattern.
330
- - \`path\`: Required. Directory or file path (use \`.\` for repo root)
331
- - \`pattern\`: Optional regex to filter results
354
+ ### tools
355
+ Tool calls use nested XML elements:
356
+ \`\`\`xml
357
+ <tool_name>
358
+ <parameter>value</parameter>
359
+ </tool_name>
360
+ \`\`\`
361
+
362
+ ### \`list_directory\`
363
+ Directory tree view. Shows structure of a path, optionally filtered by regex pattern.
364
+
365
+ Elements:
366
+ - \`<path>\` (required): Directory path to list (use \`.\` for repo root)
367
+ - \`<pattern>\` (optional): Regex to filter results
332
368
 
333
369
  Examples:
334
370
  \`\`\`
335
- analyse .
336
- analyse src/api
337
- analyse . ".*\\.ts$"
338
- analyse src "test.*"
371
+ <list_directory>
372
+ <path>src/services</path>
373
+ </list_directory>
374
+
375
+ <list_directory>
376
+ <path>lib/utils</path>
377
+ <pattern>.*\\.(ts|js)$</pattern>
378
+ </list_directory>
339
379
  \`\`\`
340
380
 
341
- ### \`read <path>[:start-end]\`
342
- Read file contents. Line range is 1-based, inclusive.
381
+ ### \`read\`
382
+ Read file contents. Supports multiple line ranges.
343
383
  - Returns numbered lines for easy reference
344
- - Omit range to read entire file
384
+ - ALWAYS include import statements (usually lines 1-20). Better to over-include than miss context.
385
+
386
+ Elements:
387
+ - \`<path>\` (required): File path to read
388
+ - \`<lines>\` (optional): Line ranges like "1-50,75-80,100-120" (omit to read entire file)
345
389
 
346
390
  Examples:
347
391
  \`\`\`
348
- read src/main.py
349
- read src/db/conn.py:10-50
350
- read package.json:1-20
392
+ <read>
393
+ <path>src/main.py</path>
394
+ </read>
395
+
396
+ <read>
397
+ <path>src/auth.py</path>
398
+ <lines>1-20,45-80,150-200</lines>
399
+ </read>
351
400
  \`\`\`
352
401
 
353
- ### \`grep '<pattern>' <path>\`
354
- Ripgrep search. Finds pattern matches across files.
355
- - \`'<pattern>'\`: Required. Regex pattern wrapped in single quotes
356
- - \`<path>\`: Required. Directory or file to search (use \`.\` for repo root)
402
+ ### \`grep\`
403
+ Search for pattern matches across files. Returns matches with 1 line of context above and below.
404
+ - Match lines use \`:\` separator \u2192 \`filepath:linenum:content\`
405
+ - Context lines use \`-\` separator \u2192 \`filepath-linenum-content\`
406
+
407
+ Elements:
408
+ - \`<pattern>\` (required): Search pattern (regex). Use \`(a|b)\` for OR patterns.
409
+ - \`<sub_dir>\` (optional): Subdirectory to search in (defaults to \`.\`)
410
+ - \`<glob>\` (optional): File pattern filter like \`*.py\` or \`*.{ts,tsx}\`
357
411
 
358
412
  Examples:
359
413
  \`\`\`
360
- grep 'class.*Service' src/
361
- grep 'def authenticate' .
362
- grep 'import.*from' src/components/
363
- grep 'TODO' .
414
+ <grep>
415
+ <pattern>(authenticate|authorize|login)</pattern>
416
+ <sub_dir>src/auth/</sub_dir>
417
+ </grep>
418
+
419
+ <grep>
420
+ <pattern>class.*(Service|Controller)</pattern>
421
+ <glob>*.{ts,js}</glob>
422
+ </grep>
423
+
424
+ <grep>
425
+ <pattern>(DB_HOST|DATABASE_URL|connection)</pattern>
426
+ <glob>*.{py,yaml,env}</glob>
427
+ <sub_dir>lib/</sub_dir>
428
+ </grep>
364
429
  \`\`\`
365
430
 
366
- ### \`finish <file1:ranges> [file2:ranges ...]\`
367
- Submit final answer with all relevant code locations.
368
- - Include generous line ranges\u2014don't be stingy with context
369
- - Ranges are comma-separated: \`file.py:10-30,50-60\`
370
- - ALWAYS include import statements at the top of files (usually lines 1-20)
371
- - If code spans multiple files, include ALL of them
372
- - Small files can be returned in full
431
+ ### \`finish\`
432
+ Submit final answer with all relevant code locations. Uses nested \`<file>\` elements.
433
+
434
+ File elements:
435
+ - \`<path>\` (required): File path
436
+ - \`<lines>\` (optional): Line ranges like "1-50,75-80" (\`*\` for entire file)
437
+
438
+ ALWAYS include import statements (usually lines 1-20). Better to over-include than miss context.
373
439
 
374
440
  Examples:
375
441
  \`\`\`
376
- finish src/auth.py:1-15,25-50,75-80 src/models/user.py:1-10,20-45
377
- finish src/index.ts:1-100
442
+ <finish>
443
+ <file>
444
+ <path>src/auth.py</path>
445
+ <lines>1-15,25-50,75-80</lines>
446
+ </file>
447
+ <file>
448
+ <path>src/models/user.py</path>
449
+ <lines>*</lines>
450
+ </file>
451
+ </finish>
378
452
  \`\`\`
379
453
  </tools>
380
454
 
381
455
  <strategy>
382
- **Before your first tool call, classify the query:**
456
+ **Before your first tool call, classify the search_string:**
383
457
 
384
- | Query Type | Turn 1 Strategy | Early Finish? |
385
- |------------|-----------------|---------------|
386
- | **Specific** (function name, error string, unique identifier) | 8 parallel greps on likely paths | Often by turn 2 |
387
- | **Conceptual** (how does X work, where is Y handled) | analyse + 2-3 broad greps | Rarely early |
388
- | **Exploratory** (find all tests, list API endpoints) | analyse at multiple depths | Usually needs 3 turns |
458
+ | Search_string Type | Round 1 Strategy | Early Finish? |
459
+ |------------|------------------|---------------|
460
+ | **Specific** (function name, error string, unique identifier) | 8 parallel greps on likely paths | Often by round 2 |
461
+ | **Conceptual** (how does X work, where is Y handled) | list_directory + 2-3 broad greps | Rarely early |
462
+ | **Exploratory** (find all tests, list API endpoints) | list_directory at multiple depths | Usually needs 3 rounds |
389
463
 
390
464
  **Parallel call patterns:**
391
465
  - **Shotgun grep**: Same pattern, 8 different directories\u2014fast coverage
392
466
  - **Variant grep**: 8 pattern variations (synonyms, naming conventions)\u2014catches inconsistent codebases
393
- - **Funnel**: 1 analyse + 7 greps\u2014orient and search simultaneously
467
+ - **Funnel**: 1 list_directory + 7 greps\u2014orient and search simultaneously
394
468
  - **Deep read**: 8 reads on files you already identified\u2014gather full context fast
469
+
470
+ **Tool call expectations:**
471
+ - Low quality tool calls are ones that give back sparse information. This either means they are not well thought out and are not educated guesses OR, they are too broad and give back too many results.
472
+ - High quality tool calls strike a balance between complexity in the tool call to exclude results we know we don't want, and how wide the search space is so that we don't miss anything. It is ok to start off with wider search spaces, but is imperative that you use your intuition from there on out and seek high quality tool calls only.
473
+ - You are not starting blind, you have some information about root level repo structure going in, so use that to prevent making trivial repo wide queries.
474
+ - The grep tool shows you which file path and line numbers the pattern was found in, use this information smartly when trying to read the file.
395
475
  </strategy>
396
476
 
397
477
  <output_format>
398
478
  EVERY response MUST follow this exact format:
399
479
 
400
480
  1. First, wrap your reasoning in \`<think>...</think>\` tags containing:
401
- - Query classification (specific/conceptual/exploratory)
402
- - Confidence estimate (can I finish in 1-2 turns?)
403
- - This turn's parallel strategy
481
+ - Search_string classification (specific/conceptual/exploratory)
482
+ - Confidence estimate (can I finish in 1-2 rounds?)
483
+ - This round's parallel strategy
404
484
  - What signals would let me finish early?
405
485
 
406
- 2. Then, output tool calls wrapped in \`<tool_call>...</tool_call>\` tags, one per line.
486
+ 2. Then, output up to 8 tool calls using nested XML elements.
407
487
 
408
488
  Example:
409
489
  \`\`\`
410
490
  <think>
411
- This is a specific query about authentication. I'll grep for auth-related patterns.
412
- High confidence I can finish in 2 turns if I find the auth module.
491
+ This is a specific search_string about authentication. I'll grep for auth-related patterns.
492
+ High confidence I can finish in 2 rounds if I find the auth module. I have already been shown the repo's structure at root
413
493
  Strategy: Shotgun grep across likely directories.
414
494
  </think>
415
- <tool_call>grep 'authenticate' src/</tool_call>
416
- <tool_call>grep 'login' src/</tool_call>
417
- <tool_call>analyse src/auth</tool_call>
495
+ <grep>
496
+ <pattern>(authenticate|login|session)</pattern>
497
+ <sub_dir>src/auth/</sub_dir>
498
+ </grep>
499
+ <grep>
500
+ <pattern>(middleware|interceptor)</pattern>
501
+ <glob>*.{ts,js}</glob>
502
+ </grep>
503
+ <list_directory>
504
+ <path>src/auth</path>
505
+ </list_directory>
506
+ \`\`\`
507
+
508
+ Finishing example:
509
+ \`\`\`
510
+ <think>
511
+ I think I have a rough idea, but this is my last turn so I must call the finish tool regardless.
512
+ </think>
513
+ <finish>
514
+ <file>
515
+ <path>src/auth/login.py</path>
516
+ <lines>1-50</lines>
517
+ </file>
518
+ <file>
519
+ <path>src/middleware/session.py</path>
520
+ <lines>10-80</lines>
521
+ </file>
522
+ </finish>
418
523
  \`\`\`
419
524
 
420
525
  No commentary outside \`<think>\`. No explanations after tool calls.
@@ -427,15 +532,116 @@ When calling \`finish\`:
427
532
  - Include any type definitions, interfaces, or constants used
428
533
  - Better to over-include than leave the user missing context
429
534
  - If unsure about boundaries, include more rather than less
430
- </finishing_requirements>
535
+ </finishing_requirements>`;
536
+ function getSystemPrompt() {
537
+ return SYSTEM_PROMPT;
538
+ }
539
+
540
+ // tools/warp_grep/agent/formatter.ts
541
+ var ToolOutputFormatter = class {
542
+ format(toolName, args, output, options = {}) {
543
+ const name = (toolName ?? "").trim();
544
+ if (!name) {
545
+ return "";
546
+ }
547
+ const payload = output?.toString?.()?.trim?.() ?? "";
548
+ const isError = Boolean(options.isError);
549
+ const safeArgs = args ?? {};
550
+ if (!payload && !isError) {
551
+ return "";
552
+ }
553
+ switch (name) {
554
+ case "read":
555
+ return this.formatRead(safeArgs, payload, isError);
556
+ case "list_directory":
557
+ return this.formatListDirectory(safeArgs, payload, isError);
558
+ case "grep":
559
+ return this.formatGrep(safeArgs, payload, isError);
560
+ default:
561
+ return payload ? `<tool_output>
562
+ ${payload}
563
+ </tool_output>` : "";
564
+ }
565
+ }
566
+ formatRead(args, payload, isError) {
567
+ if (isError) {
568
+ return payload;
569
+ }
570
+ const path4 = this.asString(args.path) || "...";
571
+ const start = args.start;
572
+ const end = args.end;
573
+ const linesArray = args.lines;
574
+ const attributes = [`path="${path4}"`];
575
+ if (linesArray && linesArray.length > 0) {
576
+ const rangeStr = linesArray.map(([s, e]) => `${s}-${e}`).join(",");
577
+ attributes.push(`lines="${rangeStr}"`);
578
+ } else if (start !== void 0 && end !== void 0) {
579
+ attributes.push(`lines="${start}-${end}"`);
580
+ }
581
+ return `<read ${attributes.join(" ")}>
582
+ ${payload}
583
+ </read>`;
584
+ }
585
+ formatListDirectory(args, payload, isError) {
586
+ const path4 = this.asString(args.path) || ".";
587
+ const pattern = this.asString(args.pattern);
588
+ const attributes = [`path="${path4}"`];
589
+ if (pattern) {
590
+ attributes.push(`pattern="${pattern}"`);
591
+ }
592
+ if (isError) {
593
+ attributes.push('status="error"');
594
+ }
595
+ return `<list_directory ${attributes.join(" ")}>
596
+ ${payload}
597
+ </list_directory>`;
598
+ }
599
+ formatGrep(args, payload, isError) {
600
+ const pattern = this.asString(args.pattern);
601
+ const subDir = this.asString(args.path);
602
+ const glob = this.asString(args.glob);
603
+ const attributes = [];
604
+ if (pattern !== void 0) {
605
+ attributes.push(`pattern="${pattern}"`);
606
+ }
607
+ if (subDir !== void 0) {
608
+ attributes.push(`sub_dir="${subDir}"`);
609
+ }
610
+ if (glob !== void 0) {
611
+ attributes.push(`glob="${glob}"`);
612
+ }
613
+ if (isError) {
614
+ attributes.push('status="error"');
615
+ }
616
+ const attrText = attributes.length ? ` ${attributes.join(" ")}` : "";
617
+ return `<grep${attrText}>
618
+ ${payload}
619
+ </grep>`;
620
+ }
621
+ asString(value) {
622
+ if (value === null || value === void 0) {
623
+ return void 0;
624
+ }
625
+ return String(value);
626
+ }
627
+ };
628
+ var sharedFormatter = new ToolOutputFormatter();
629
+ function formatAgentToolOutput(toolName, args, output, options = {}) {
630
+ return sharedFormatter.format(toolName, args, output, options);
631
+ }
431
632
 
432
- Begin your exploration now to find code relevant to the query.`;
633
+ // tools/warp_grep/agent/helpers.ts
634
+ var import_path = __toESM(require("path"), 1);
433
635
 
434
636
  // tools/warp_grep/agent/config.ts
435
637
  var AGENT_CONFIG = {
436
- // Give the model freedom; failsafe cap to prevent infinite loops
437
- MAX_ROUNDS: 10,
438
- TIMEOUT_MS: 3e4
638
+ MAX_TURNS: 4,
639
+ TIMEOUT_MS: 3e4,
640
+ MAX_CONTEXT_CHARS: 54e4,
641
+ MAX_OUTPUT_LINES: 200,
642
+ MAX_READ_LINES: 800,
643
+ MAX_LIST_DEPTH: 3,
644
+ LIST_TIMEOUT_MS: 2e3
439
645
  };
440
646
  var BUILTIN_EXCLUDES = [
441
647
  // Version control
@@ -518,7 +724,124 @@ var BUILTIN_EXCLUDES = [
518
724
  ];
519
725
  var DEFAULT_EXCLUDES = (process.env.MORPH_WARP_GREP_EXCLUDE || "").split(",").map((s) => s.trim()).filter(Boolean).concat(BUILTIN_EXCLUDES);
520
726
 
521
- // tools/warp_grep/tools/finish.ts
727
+ // tools/warp_grep/agent/helpers.ts
728
+ function formatTurnMessage(turnsUsed, maxTurns) {
729
+ const turnsRemaining = maxTurns - turnsUsed;
730
+ if (turnsRemaining === 1) {
731
+ return `
732
+ You have used ${turnsUsed} turns, you only have 1 turn remaining. You have run out of turns to explore the code base and MUST call the finish tool now`;
733
+ }
734
+ return `
735
+ You have used ${turnsUsed} turn${turnsUsed === 1 ? "" : "s"} and have ${turnsRemaining} remaining`;
736
+ }
737
+ function calculateContextBudget(messages) {
738
+ const totalChars = messages.reduce((sum, m) => sum + m.content.length, 0);
739
+ const maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS;
740
+ const percent = Math.round(totalChars / maxChars * 100);
741
+ const usedK = Math.round(totalChars / 1e3);
742
+ const maxK = Math.round(maxChars / 1e3);
743
+ return `<context_budget>${percent}% (${usedK}K/${maxK}K chars)</context_budget>`;
744
+ }
745
+ async function buildInitialState(repoRoot, query, provider) {
746
+ try {
747
+ const entries = await provider.listDirectory({
748
+ path: ".",
749
+ maxResults: AGENT_CONFIG.MAX_OUTPUT_LINES,
750
+ maxDepth: 2
751
+ });
752
+ const treeLines = entries.map((e) => {
753
+ const indent = " ".repeat(e.depth);
754
+ const name = e.type === "dir" ? `${e.name}/` : e.name;
755
+ return `${indent}${name}`;
756
+ });
757
+ const repoName = import_path.default.basename(repoRoot);
758
+ const treeOutput = treeLines.length > 0 ? `${repoName}/
759
+ ${treeLines.join("\n")}` : `${repoName}/`;
760
+ return `<repo_structure>
761
+ ${treeOutput}
762
+ </repo_structure>
763
+
764
+ <search_string>
765
+ ${query}
766
+ </search_string>`;
767
+ } catch {
768
+ const repoName = import_path.default.basename(repoRoot);
769
+ return `<repo_structure>
770
+ ${repoName}/
771
+ </repo_structure>
772
+
773
+ <search_string>
774
+ ${query}
775
+ </search_string>`;
776
+ }
777
+ }
778
+ function formatListDirectoryTree(entries) {
779
+ if (!entries.length) return "empty";
780
+ return entries.map((e) => {
781
+ const indent = " ".repeat(e.depth);
782
+ const name = e.type === "dir" ? `${e.name}/` : e.name;
783
+ return `${indent}${name}`;
784
+ }).join("\n");
785
+ }
786
+
787
+ // tools/warp_grep/agent/tools/grep.ts
788
+ async function toolGrep(provider, args) {
789
+ const res = await provider.grep(args);
790
+ if (res.error) {
791
+ return { output: res.error };
792
+ }
793
+ if (!res.lines.length) {
794
+ return { output: "no matches" };
795
+ }
796
+ return { output: res.lines.join("\n") };
797
+ }
798
+
799
+ // tools/warp_grep/agent/tools/read.ts
800
+ async function toolRead(provider, args) {
801
+ if (args.lines && args.lines.length > 0) {
802
+ const chunks = [];
803
+ for (const [start, end] of args.lines) {
804
+ const res2 = await provider.read({ path: args.path, start, end });
805
+ if (res2.error) return res2.error;
806
+ chunks.push(res2.lines.join("\n"));
807
+ }
808
+ if (chunks.every((c) => c === "")) return "(empty file)";
809
+ return chunks.join("\n...\n");
810
+ }
811
+ const res = await provider.read({ path: args.path, start: args.start, end: args.end });
812
+ if (res.error) {
813
+ return res.error;
814
+ }
815
+ if (!res.lines.length) return "(empty file)";
816
+ return res.lines.join("\n");
817
+ }
818
+
819
+ // tools/warp_grep/agent/tools/list_directory.ts
820
+ async function toolListDirectory(provider, args) {
821
+ const list = await provider.listDirectory({
822
+ path: args.path,
823
+ pattern: args.pattern ?? null,
824
+ maxResults: args.maxResults ?? AGENT_CONFIG.MAX_OUTPUT_LINES,
825
+ maxDepth: args.maxDepth ?? AGENT_CONFIG.MAX_LIST_DEPTH
826
+ });
827
+ if (!list.length) return "empty";
828
+ if (list.length >= AGENT_CONFIG.MAX_OUTPUT_LINES) {
829
+ return "query not specific enough, tool called tried to return too much context and failed";
830
+ }
831
+ return list.map((e) => {
832
+ const indent = " ".repeat(e.depth);
833
+ const name = e.type === "dir" ? `${e.name}/` : e.name;
834
+ return `${indent}${name}`;
835
+ }).join("\n");
836
+ }
837
+
838
+ // tools/warp_grep/agent/tools/finish.ts
839
+ function normalizeFinishFiles(files) {
840
+ return files.map((f) => {
841
+ const merged = mergeRanges(f.lines);
842
+ return { path: f.path, lines: merged };
843
+ });
844
+ }
522
845
  async function readFinishFiles(repoRoot, files, reader) {
523
846
  const out = [];
524
847
  for (const f of files) {
@@ -553,7 +876,7 @@ function mergeRanges(ranges) {
553
876
 
554
877
  // tools/warp_grep/providers/local.ts
555
878
  var import_promises2 = __toESM(require("fs/promises"), 1);
556
- var import_path2 = __toESM(require("path"), 1);
879
+ var import_path3 = __toESM(require("path"), 1);
557
880
 
558
881
  // tools/warp_grep/utils/ripgrep.ts
559
882
  var import_child_process = require("child_process");
@@ -618,21 +941,21 @@ async function runRipgrep(args, opts) {
618
941
 
619
942
  // tools/warp_grep/utils/paths.ts
620
943
  var import_fs = __toESM(require("fs"), 1);
621
- var import_path = __toESM(require("path"), 1);
944
+ var import_path2 = __toESM(require("path"), 1);
622
945
  function resolveUnderRepo(repoRoot, targetPath) {
623
- const absRoot = import_path.default.resolve(repoRoot);
624
- const resolved = import_path.default.resolve(absRoot, targetPath);
946
+ const absRoot = import_path2.default.resolve(repoRoot);
947
+ const resolved = import_path2.default.resolve(absRoot, targetPath);
625
948
  ensureWithinRepo(absRoot, resolved);
626
949
  return resolved;
627
950
  }
628
951
  function ensureWithinRepo(repoRoot, absTarget) {
629
- const rel = import_path.default.relative(import_path.default.resolve(repoRoot), import_path.default.resolve(absTarget));
630
- if (rel.startsWith("..") || import_path.default.isAbsolute(rel)) {
952
+ const rel = import_path2.default.relative(import_path2.default.resolve(repoRoot), import_path2.default.resolve(absTarget));
953
+ if (rel.startsWith("..") || import_path2.default.isAbsolute(rel)) {
631
954
  throw new Error(`Path outside repository root: ${absTarget}`);
632
955
  }
633
956
  }
634
957
  function toRepoRelative(repoRoot, absPath) {
635
- return import_path.default.relative(import_path.default.resolve(repoRoot), import_path.default.resolve(absPath));
958
+ return import_path2.default.relative(import_path2.default.resolve(repoRoot), import_path2.default.resolve(absPath));
636
959
  }
637
960
  function isSymlink(p) {
638
961
  try {
@@ -675,10 +998,18 @@ var LocalRipgrepProvider = class {
675
998
  this.excludes = excludes;
676
999
  }
677
1000
  async grep(params) {
678
- const abs = resolveUnderRepo(this.repoRoot, params.path);
1001
+ let abs;
1002
+ try {
1003
+ abs = resolveUnderRepo(this.repoRoot, params.path);
1004
+ } catch (err) {
1005
+ return {
1006
+ lines: [],
1007
+ error: `[PATH ERROR] ${err instanceof Error ? err.message : String(err)}`
1008
+ };
1009
+ }
679
1010
  const stat = await import_promises2.default.stat(abs).catch(() => null);
680
1011
  if (!stat) return { lines: [] };
681
- const targetArg = abs === import_path2.default.resolve(this.repoRoot) ? "." : toRepoRelative(this.repoRoot, abs);
1012
+ const targetArg = abs === import_path3.default.resolve(this.repoRoot) ? "." : toRepoRelative(this.repoRoot, abs);
682
1013
  const args = [
683
1014
  "--no-config",
684
1015
  "--no-heading",
@@ -687,6 +1018,9 @@ var LocalRipgrepProvider = class {
687
1018
  "--color=never",
688
1019
  "--trim",
689
1020
  "--max-columns=400",
1021
+ "-C",
1022
+ "1",
1023
+ ...params.glob ? ["--glob", params.glob] : [],
690
1024
  ...this.excludes.flatMap((e) => ["-g", `!${e}`]),
691
1025
  params.pattern,
692
1026
  targetArg || "."
@@ -711,29 +1045,24 @@ Details: ${res.stderr}` : ""}`
711
1045
  };
712
1046
  }
713
1047
  const lines = (res.stdout || "").trim().split(/\r?\n/).filter((l) => l.length > 0);
714
- return { lines };
715
- }
716
- async glob(params) {
717
- const abs = resolveUnderRepo(this.repoRoot, params.path);
718
- const targetArg = abs === import_path2.default.resolve(this.repoRoot) ? "." : toRepoRelative(this.repoRoot, abs);
719
- const args = [
720
- "--no-config",
721
- "--files",
722
- "-g",
723
- params.pattern,
724
- ...this.excludes.flatMap((e) => ["-g", `!${e}`]),
725
- targetArg || "."
726
- ];
727
- const res = await runRipgrep(args, { cwd: this.repoRoot });
728
- if (res.exitCode === -1) {
729
- console.warn(`[warp_grep] ripgrep not available for glob: ${res.stderr || "execution failed"}`);
730
- return { files: [] };
1048
+ if (lines.length > AGENT_CONFIG.MAX_OUTPUT_LINES) {
1049
+ return {
1050
+ lines: [],
1051
+ error: "query not specific enough, tool tried to return too much context and failed"
1052
+ };
731
1053
  }
732
- const files = (res.stdout || "").trim().split(/\r?\n/).filter((l) => l.length > 0);
733
- return { files };
1054
+ return { lines };
734
1055
  }
735
1056
  async read(params) {
736
- const abs = resolveUnderRepo(this.repoRoot, params.path);
1057
+ let abs;
1058
+ try {
1059
+ abs = resolveUnderRepo(this.repoRoot, params.path);
1060
+ } catch (err) {
1061
+ return {
1062
+ lines: [],
1063
+ error: `[PATH ERROR] ${err instanceof Error ? err.message : String(err)}`
1064
+ };
1065
+ }
737
1066
  const stat = await import_promises2.default.stat(abs).catch(() => null);
738
1067
  if (!stat || !stat.isFile()) {
739
1068
  return {
@@ -753,7 +1082,15 @@ Details: ${res.stderr}` : ""}`
753
1082
  error: `[UNREADABLE FILE] You tried to read "${params.path}" but this file is either too large or not a text file, so it cannot be read. Try a different file.`
754
1083
  };
755
1084
  }
756
- const lines = await readAllLines(abs);
1085
+ let lines;
1086
+ try {
1087
+ lines = await readAllLines(abs);
1088
+ } catch (err) {
1089
+ return {
1090
+ lines: [],
1091
+ error: `[READ ERROR] Failed to read "${params.path}": ${err instanceof Error ? err.message : String(err)}`
1092
+ };
1093
+ }
757
1094
  const total = lines.length;
758
1095
  let s = params.start ?? 1;
759
1096
  let e = Math.min(params.end ?? total, total);
@@ -766,30 +1103,46 @@ Details: ${res.stderr}` : ""}`
766
1103
  const content = lines[i - 1] ?? "";
767
1104
  out.push(`${i}|${content}`);
768
1105
  }
1106
+ if (out.length > AGENT_CONFIG.MAX_READ_LINES) {
1107
+ const truncated = out.slice(0, AGENT_CONFIG.MAX_READ_LINES);
1108
+ truncated.push(`... [truncated: showing ${AGENT_CONFIG.MAX_READ_LINES} of ${out.length} lines]`);
1109
+ return { lines: truncated };
1110
+ }
769
1111
  return { lines: out };
770
1112
  }
771
- async analyse(params) {
772
- const abs = resolveUnderRepo(this.repoRoot, params.path);
1113
+ async listDirectory(params) {
1114
+ let abs;
1115
+ try {
1116
+ abs = resolveUnderRepo(this.repoRoot, params.path);
1117
+ } catch {
1118
+ return [];
1119
+ }
773
1120
  const stat = await import_promises2.default.stat(abs).catch(() => null);
774
1121
  if (!stat || !stat.isDirectory()) {
775
1122
  return [];
776
1123
  }
777
- const maxResults = params.maxResults ?? 100;
778
- const maxDepth = params.maxDepth ?? 2;
1124
+ const maxResults = params.maxResults ?? AGENT_CONFIG.MAX_OUTPUT_LINES;
1125
+ const maxDepth = params.maxDepth ?? AGENT_CONFIG.MAX_LIST_DEPTH;
779
1126
  const regex = params.pattern ? new RegExp(params.pattern) : null;
780
1127
  const results = [];
1128
+ let timedOut = false;
1129
+ const startTime = Date.now();
781
1130
  async function walk(dir, depth) {
1131
+ if (Date.now() - startTime > AGENT_CONFIG.LIST_TIMEOUT_MS) {
1132
+ timedOut = true;
1133
+ return;
1134
+ }
782
1135
  if (depth > maxDepth || results.length >= maxResults) return;
783
1136
  const entries = await import_promises2.default.readdir(dir, { withFileTypes: true });
784
1137
  for (const entry of entries) {
785
- const full = import_path2.default.join(dir, entry.name);
1138
+ if (timedOut || results.length >= maxResults) break;
1139
+ const full = import_path3.default.join(dir, entry.name);
786
1140
  const rel = toRepoRelative(abs, full).replace(/^[.][/\\]?/, "");
787
- if (DEFAULT_EXCLUDES.some((ex) => rel.split(import_path2.default.sep).includes(ex))) continue;
1141
+ if (DEFAULT_EXCLUDES.some((ex) => rel.split(import_path3.default.sep).includes(ex))) continue;
788
1142
  if (regex && !regex.test(entry.name)) continue;
789
- if (results.length >= maxResults) break;
790
1143
  results.push({
791
1144
  name: entry.name,
792
- path: toRepoRelative(import_path2.default.resolve(""), full),
1145
+ path: toRepoRelative(import_path3.default.resolve(""), full),
793
1146
  // relative display
794
1147
  type: entry.isDirectory() ? "dir" : "file",
795
1148
  depth
@@ -809,29 +1162,6 @@ var parser = new LLMResponseParser();
809
1162
  function parseToolCalls(text) {
810
1163
  return parser.parse(text);
811
1164
  }
812
- function formatToolResult(name, args, output, options) {
813
- return formatAgentToolOutput(name, args, output, options ?? {});
814
- }
815
- function formatTurnMessage(turn, maxTurns = 4) {
816
- const remaining = maxTurns - turn;
817
- if (remaining === 0) {
818
- return `
819
-
820
- [Turn ${turn}/${maxTurns}] This is your LAST turn. You MUST call finish now.`;
821
- }
822
- if (remaining === 1) {
823
- return `
824
-
825
- [Turn ${turn}/${maxTurns}] You have 1 turn remaining.`;
826
- }
827
- return `
828
-
829
- [Turn ${turn}/${maxTurns}] You have ${remaining} turns remaining.`;
830
- }
831
- function formatAnalyseTree(entries) {
832
- if (!entries.length) return "empty";
833
- return entries.map((e) => `${" ".repeat(e.depth)}- ${e.type === "dir" ? "[D]" : "[F]"} ${e.name}`).join("\n");
834
- }
835
1165
  async function resolveFinishFiles(provider, files) {
836
1166
  return readFinishFiles("", files, async (p, s, e) => {
837
1167
  const r = await provider.read({ path: p, start: s, end: e });
@@ -841,19 +1171,25 @@ async function resolveFinishFiles(provider, files) {
841
1171
  });
842
1172
  });
843
1173
  }
844
- var MAX_TURNS = 4;
845
- var TIMEOUT_MS = AGENT_CONFIG.TIMEOUT_MS;
846
1174
  // Annotate the CommonJS export names for ESM import in node:
847
1175
  0 && (module.exports = {
1176
+ AGENT_CONFIG,
848
1177
  DEFAULT_EXCLUDES,
1178
+ LLMResponseParser,
849
1179
  LocalRipgrepProvider,
850
- MAX_TURNS,
851
1180
  SYSTEM_PROMPT,
852
- TIMEOUT_MS,
853
- formatAnalyseTree,
1181
+ buildInitialState,
1182
+ calculateContextBudget,
1183
+ formatListDirectoryTree,
854
1184
  formatToolResult,
855
1185
  formatTurnMessage,
1186
+ getSystemPrompt,
1187
+ normalizeFinishFiles,
856
1188
  parseToolCalls,
857
- resolveFinishFiles
1189
+ readFinishFiles,
1190
+ resolveFinishFiles,
1191
+ toolGrep,
1192
+ toolListDirectory,
1193
+ toolRead
858
1194
  });
859
1195
  //# sourceMappingURL=harness.cjs.map