@morphllm/morphsdk 0.2.57 → 0.2.58
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.
- package/dist/anthropic-CaFUHxBW.d.ts +89 -0
- package/dist/{chunk-6X5UOY7B.js → chunk-2CASO3ZO.js} +46 -79
- package/dist/chunk-2CASO3ZO.js.map +1 -0
- package/dist/chunk-374N3GIA.js +118 -0
- package/dist/chunk-374N3GIA.js.map +1 -0
- package/dist/chunk-3IQIT6MC.js +65 -0
- package/dist/chunk-3IQIT6MC.js.map +1 -0
- package/dist/chunk-4VGOBA2J.js +57 -0
- package/dist/chunk-4VGOBA2J.js.map +1 -0
- package/dist/chunk-527P5X2E.js +98 -0
- package/dist/chunk-527P5X2E.js.map +1 -0
- package/dist/chunk-6N6ZYZYD.js +74 -0
- package/dist/chunk-6N6ZYZYD.js.map +1 -0
- package/dist/chunk-6Y5JB4JC.js +195 -0
- package/dist/chunk-6Y5JB4JC.js.map +1 -0
- package/dist/{chunk-QFIHUCTF.js → chunk-7EIHYJSG.js} +18 -18
- package/dist/chunk-7EIHYJSG.js.map +1 -0
- package/dist/{chunk-TICMYDII.js → chunk-APP75CBN.js} +33 -16
- package/dist/chunk-APP75CBN.js.map +1 -0
- package/dist/chunk-ILJ3J5IA.js +72 -0
- package/dist/chunk-ILJ3J5IA.js.map +1 -0
- package/dist/chunk-ISWL67SF.js +1 -0
- package/dist/chunk-KW7OEGZK.js +9 -0
- package/dist/chunk-KW7OEGZK.js.map +1 -0
- package/dist/chunk-Q5AHGIQO.js +205 -0
- package/dist/chunk-Q5AHGIQO.js.map +1 -0
- package/dist/{chunk-TJIUA27P.js → chunk-XT5ZO6ES.js} +9 -5
- package/dist/chunk-XT5ZO6ES.js.map +1 -0
- package/dist/{chunk-LVPVVLTI.js → chunk-YV75OQTE.js} +105 -17
- package/dist/chunk-YV75OQTE.js.map +1 -0
- package/dist/{chunk-ZJIIICRA.js → chunk-ZO4PPFCZ.js} +60 -29
- package/dist/chunk-ZO4PPFCZ.js.map +1 -0
- package/dist/{client-CFoR--IU.d.ts → client-CextMMm9.d.ts} +10 -15
- package/dist/client.cjs +687 -341
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.ts +3 -2
- package/dist/client.js +14 -14
- package/dist/finish-kXAcUJyB.d.ts +33 -0
- package/dist/gemini-CE80Pbdy.d.ts +117 -0
- package/dist/index.cjs +700 -341
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.js +16 -15
- package/dist/openai-Fvpqln7F.d.ts +89 -0
- package/dist/tools/warp_grep/agent/config.cjs +8 -4
- package/dist/tools/warp_grep/agent/config.cjs.map +1 -1
- package/dist/tools/warp_grep/agent/config.d.ts +7 -2
- package/dist/tools/warp_grep/agent/config.js +1 -1
- package/dist/tools/warp_grep/agent/formatter.cjs +32 -15
- package/dist/tools/warp_grep/agent/formatter.cjs.map +1 -1
- package/dist/tools/warp_grep/agent/formatter.d.ts +1 -1
- package/dist/tools/warp_grep/agent/formatter.js +1 -1
- package/dist/tools/warp_grep/agent/parser.cjs +104 -17
- package/dist/tools/warp_grep/agent/parser.cjs.map +1 -1
- package/dist/tools/warp_grep/agent/parser.d.ts +3 -5
- package/dist/tools/warp_grep/agent/parser.js +1 -3
- package/dist/tools/warp_grep/agent/prompt.cjs +132 -56
- package/dist/tools/warp_grep/agent/prompt.cjs.map +1 -1
- package/dist/tools/warp_grep/agent/prompt.d.ts +1 -1
- package/dist/tools/warp_grep/agent/prompt.js +1 -1
- package/dist/tools/warp_grep/agent/runner.cjs +459 -192
- package/dist/tools/warp_grep/agent/runner.cjs.map +1 -1
- package/dist/tools/warp_grep/agent/runner.d.ts +1 -0
- package/dist/tools/warp_grep/agent/runner.js +6 -8
- package/dist/tools/warp_grep/agent/types.cjs.map +1 -1
- package/dist/tools/warp_grep/agent/types.d.ts +9 -2
- package/dist/tools/warp_grep/anthropic.cjs +650 -260
- package/dist/tools/warp_grep/anthropic.cjs.map +1 -1
- package/dist/tools/warp_grep/anthropic.d.ts +4 -74
- package/dist/tools/warp_grep/anthropic.js +13 -15
- package/dist/tools/warp_grep/client.cjs +1593 -0
- package/dist/tools/warp_grep/client.cjs.map +1 -0
- package/dist/tools/warp_grep/client.d.ts +87 -0
- package/dist/tools/warp_grep/client.js +26 -0
- package/dist/tools/warp_grep/gemini.cjs +1587 -0
- package/dist/tools/warp_grep/gemini.cjs.map +1 -0
- package/dist/tools/warp_grep/gemini.d.ts +7 -0
- package/dist/tools/warp_grep/gemini.js +34 -0
- package/dist/tools/warp_grep/harness.cjs +556 -220
- package/dist/tools/warp_grep/harness.cjs.map +1 -1
- package/dist/tools/warp_grep/harness.d.ts +50 -119
- package/dist/tools/warp_grep/harness.js +33 -41
- package/dist/tools/warp_grep/harness.js.map +1 -1
- package/dist/tools/warp_grep/index.cjs +812 -346
- package/dist/tools/warp_grep/index.cjs.map +1 -1
- package/dist/tools/warp_grep/index.d.ts +11 -6
- package/dist/tools/warp_grep/index.js +43 -22
- package/dist/tools/warp_grep/openai.cjs +650 -258
- package/dist/tools/warp_grep/openai.cjs.map +1 -1
- package/dist/tools/warp_grep/openai.d.ts +4 -74
- package/dist/tools/warp_grep/openai.js +13 -13
- package/dist/tools/warp_grep/providers/local.cjs +66 -27
- package/dist/tools/warp_grep/providers/local.cjs.map +1 -1
- package/dist/tools/warp_grep/providers/local.d.ts +4 -9
- package/dist/tools/warp_grep/providers/local.js +2 -2
- package/dist/tools/warp_grep/providers/remote.cjs +211 -0
- package/dist/tools/warp_grep/providers/remote.cjs.map +1 -0
- package/dist/tools/warp_grep/providers/remote.d.ts +67 -0
- package/dist/tools/warp_grep/providers/remote.js +9 -0
- package/dist/tools/warp_grep/providers/types.cjs.map +1 -1
- package/dist/tools/warp_grep/providers/types.d.ts +7 -15
- package/dist/tools/warp_grep/vercel.cjs +662 -277
- package/dist/tools/warp_grep/vercel.cjs.map +1 -1
- package/dist/tools/warp_grep/vercel.d.ts +4 -51
- package/dist/tools/warp_grep/vercel.js +16 -14
- package/dist/types-a_hxdPI6.d.ts +144 -0
- package/dist/vercel-3yjvfmVB.d.ts +66 -0
- package/package.json +12 -2
- package/dist/chunk-6X5UOY7B.js.map +0 -1
- package/dist/chunk-73RQWOQC.js +0 -16
- package/dist/chunk-73RQWOQC.js.map +0 -1
- package/dist/chunk-7OQOOB3R.js +0 -1
- package/dist/chunk-CFF636UC.js +0 -70
- package/dist/chunk-CFF636UC.js.map +0 -1
- package/dist/chunk-EK7OQPWD.js +0 -44
- package/dist/chunk-EK7OQPWD.js.map +0 -1
- package/dist/chunk-GJ5TYNRD.js +0 -107
- package/dist/chunk-GJ5TYNRD.js.map +0 -1
- package/dist/chunk-HQO45BAJ.js +0 -14
- package/dist/chunk-HQO45BAJ.js.map +0 -1
- package/dist/chunk-IMYQOKFO.js +0 -83
- package/dist/chunk-IMYQOKFO.js.map +0 -1
- package/dist/chunk-KBQWGT5L.js +0 -77
- package/dist/chunk-KBQWGT5L.js.map +0 -1
- package/dist/chunk-LVPVVLTI.js.map +0 -1
- package/dist/chunk-QFIHUCTF.js.map +0 -1
- package/dist/chunk-TICMYDII.js.map +0 -1
- package/dist/chunk-TJIUA27P.js.map +0 -1
- package/dist/chunk-WETRQJGU.js +0 -129
- package/dist/chunk-WETRQJGU.js.map +0 -1
- package/dist/chunk-ZJIIICRA.js.map +0 -1
- package/dist/core-CpkYEi_T.d.ts +0 -158
- package/dist/tools/warp_grep/tools/analyse.cjs +0 -40
- package/dist/tools/warp_grep/tools/analyse.cjs.map +0 -1
- package/dist/tools/warp_grep/tools/analyse.d.ts +0 -10
- package/dist/tools/warp_grep/tools/analyse.js +0 -8
- package/dist/tools/warp_grep/tools/finish.cjs +0 -69
- package/dist/tools/warp_grep/tools/finish.cjs.map +0 -1
- package/dist/tools/warp_grep/tools/finish.d.ts +0 -10
- package/dist/tools/warp_grep/tools/finish.js +0 -10
- package/dist/tools/warp_grep/tools/grep.cjs +0 -38
- package/dist/tools/warp_grep/tools/grep.cjs.map +0 -1
- package/dist/tools/warp_grep/tools/grep.d.ts +0 -8
- package/dist/tools/warp_grep/tools/grep.js +0 -15
- package/dist/tools/warp_grep/tools/grep.js.map +0 -1
- package/dist/tools/warp_grep/tools/read.cjs +0 -38
- package/dist/tools/warp_grep/tools/read.cjs.map +0 -1
- package/dist/tools/warp_grep/tools/read.d.ts +0 -9
- package/dist/tools/warp_grep/tools/read.js +0 -8
- package/dist/tools/warp_grep/utils/format.cjs +0 -42
- package/dist/tools/warp_grep/utils/format.cjs.map +0 -1
- package/dist/tools/warp_grep/utils/format.d.ts +0 -4
- package/dist/tools/warp_grep/utils/format.js +0 -18
- package/dist/tools/warp_grep/utils/format.js.map +0 -1
- /package/dist/{chunk-7OQOOB3R.js.map → chunk-ISWL67SF.js.map} +0 -0
- /package/dist/tools/warp_grep/{tools/analyse.js.map → client.js.map} +0 -0
- /package/dist/tools/warp_grep/{tools/finish.js.map → gemini.js.map} +0 -0
- /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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
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 = ["
|
|
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 "
|
|
104
|
-
this.
|
|
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(([
|
|
123
|
-
path:
|
|
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
|
-
|
|
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:
|
|
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
|
|
266
|
+
const path4 = parts[1];
|
|
163
267
|
const pattern = parts[2]?.replace(/^"|"$/g, "") ?? null;
|
|
164
|
-
commands.push({ name: "
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
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
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
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
|
|
342
|
-
Read file contents.
|
|
381
|
+
### \`read\`
|
|
382
|
+
Read file contents. Supports multiple line ranges.
|
|
343
383
|
- Returns numbered lines for easy reference
|
|
344
|
-
-
|
|
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
|
|
349
|
-
|
|
350
|
-
read
|
|
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
|
|
354
|
-
|
|
355
|
-
-
|
|
356
|
-
-
|
|
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
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
grep
|
|
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
|
|
367
|
-
Submit final answer with all relevant code locations.
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
-
|
|
371
|
-
-
|
|
372
|
-
|
|
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
|
|
377
|
-
|
|
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
|
|
456
|
+
**Before your first tool call, classify the search_string:**
|
|
383
457
|
|
|
384
|
-
|
|
|
385
|
-
|
|
386
|
-
| **Specific** (function name, error string, unique identifier) | 8 parallel greps on likely paths | Often by
|
|
387
|
-
| **Conceptual** (how does X work, where is Y handled) |
|
|
388
|
-
| **Exploratory** (find all tests, list API endpoints) |
|
|
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
|
|
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
|
-
-
|
|
402
|
-
- Confidence estimate (can I finish in 1-2
|
|
403
|
-
- This
|
|
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
|
|
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
|
|
412
|
-
High confidence I can finish in 2
|
|
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
|
-
<
|
|
416
|
-
<
|
|
417
|
-
<
|
|
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
|
-
|
|
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
|
-
|
|
437
|
-
|
|
438
|
-
|
|
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/
|
|
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
|
|
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
|
|
944
|
+
var import_path2 = __toESM(require("path"), 1);
|
|
622
945
|
function resolveUnderRepo(repoRoot, targetPath) {
|
|
623
|
-
const absRoot =
|
|
624
|
-
const resolved =
|
|
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 =
|
|
630
|
-
if (rel.startsWith("..") ||
|
|
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
|
|
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
|
-
|
|
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 ===
|
|
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
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
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
|
-
|
|
733
|
-
return { files };
|
|
1054
|
+
return { lines };
|
|
734
1055
|
}
|
|
735
1056
|
async read(params) {
|
|
736
|
-
|
|
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
|
-
|
|
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
|
|
772
|
-
|
|
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 ??
|
|
778
|
-
const maxDepth = params.maxDepth ??
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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
|
-
|
|
853
|
-
|
|
1181
|
+
buildInitialState,
|
|
1182
|
+
calculateContextBudget,
|
|
1183
|
+
formatListDirectoryTree,
|
|
854
1184
|
formatToolResult,
|
|
855
1185
|
formatTurnMessage,
|
|
1186
|
+
getSystemPrompt,
|
|
1187
|
+
normalizeFinishFiles,
|
|
856
1188
|
parseToolCalls,
|
|
857
|
-
|
|
1189
|
+
readFinishFiles,
|
|
1190
|
+
resolveFinishFiles,
|
|
1191
|
+
toolGrep,
|
|
1192
|
+
toolListDirectory,
|
|
1193
|
+
toolRead
|
|
858
1194
|
});
|
|
859
1195
|
//# sourceMappingURL=harness.cjs.map
|