@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.
- 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-TICMYDII.js → chunk-APP75CBN.js} +33 -16
- package/dist/chunk-APP75CBN.js.map +1 -0
- package/dist/{chunk-QFIHUCTF.js → chunk-FN4EP3WY.js} +19 -19
- package/dist/chunk-FN4EP3WY.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-OXHGFHEU.js → chunk-VJU3BRET.js} +3 -3
- package/dist/chunk-VJU3BRET.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 +689 -343
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.ts +3 -2
- package/dist/client.js +15 -15
- package/dist/finish-kXAcUJyB.d.ts +33 -0
- package/dist/gemini-CE80Pbdy.d.ts +117 -0
- package/dist/git/client.cjs +2 -2
- package/dist/git/client.cjs.map +1 -1
- package/dist/git/client.d.ts +1 -1
- package/dist/git/client.js +1 -1
- package/dist/git/index.cjs +2 -2
- package/dist/git/index.cjs.map +1 -1
- package/dist/git/index.js +1 -1
- package/dist/git/types.cjs.map +1 -1
- package/dist/git/types.d.ts +1 -1
- package/dist/index.cjs +702 -343
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.js +17 -16
- 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-OXHGFHEU.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
|
@@ -36,9 +36,13 @@ module.exports = __toCommonJS(runner_exports);
|
|
|
36
36
|
|
|
37
37
|
// tools/warp_grep/agent/config.ts
|
|
38
38
|
var AGENT_CONFIG = {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
MAX_TURNS: 4,
|
|
40
|
+
TIMEOUT_MS: 3e4,
|
|
41
|
+
MAX_CONTEXT_CHARS: 54e4,
|
|
42
|
+
MAX_OUTPUT_LINES: 200,
|
|
43
|
+
MAX_READ_LINES: 800,
|
|
44
|
+
MAX_LIST_DEPTH: 3,
|
|
45
|
+
LIST_TIMEOUT_MS: 2e3
|
|
42
46
|
};
|
|
43
47
|
var BUILTIN_EXCLUDES = [
|
|
44
48
|
// Version control
|
|
@@ -120,113 +124,191 @@ var BUILTIN_EXCLUDES = [
|
|
|
120
124
|
".*"
|
|
121
125
|
];
|
|
122
126
|
var DEFAULT_EXCLUDES = (process.env.MORPH_WARP_GREP_EXCLUDE || "").split(",").map((s) => s.trim()).filter(Boolean).concat(BUILTIN_EXCLUDES);
|
|
123
|
-
var DEFAULT_MODEL = "morph-warp-grep";
|
|
127
|
+
var DEFAULT_MODEL = "morph-warp-grep-v1";
|
|
124
128
|
|
|
125
129
|
// tools/warp_grep/agent/prompt.ts
|
|
126
|
-
var SYSTEM_PROMPT = `You are a code search agent. Your task is to find all relevant code for a given
|
|
130
|
+
var SYSTEM_PROMPT = `You are a code search agent. Your task is to find all relevant code for a given search_string.
|
|
127
131
|
|
|
128
|
-
|
|
132
|
+
### workflow
|
|
129
133
|
You have exactly 4 turns. The 4th turn MUST be a \`finish\` call. Each turn allows up to 8 parallel tool calls.
|
|
130
134
|
|
|
131
|
-
- Turn 1: Map the territory OR dive deep (based on
|
|
135
|
+
- Turn 1: Map the territory OR dive deep (based on search_string specificity)
|
|
132
136
|
- Turn 2-3: Refine based on findings
|
|
133
137
|
- Turn 4: MUST call \`finish\` with all relevant code locations
|
|
134
138
|
- You MAY call \`finish\` early if confident\u2014but never before at least 1 search turn.
|
|
139
|
+
- The user strongly prefers if you can call the finish tool early, but you must be correct
|
|
135
140
|
|
|
136
|
-
Remember, if the task feels easy to you, it is strongly desirable to call
|
|
137
|
-
</workflow>
|
|
141
|
+
Remember, if the task feels easy to you, it is strongly desirable to call 'finish' early using fewer turns, but quality over speed
|
|
138
142
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
143
|
+
### tools
|
|
144
|
+
Tool calls use nested XML elements:
|
|
145
|
+
\`\`\`xml
|
|
146
|
+
<tool_name>
|
|
147
|
+
<parameter>value</parameter>
|
|
148
|
+
</tool_name>
|
|
149
|
+
\`\`\`
|
|
150
|
+
|
|
151
|
+
### \`list_directory\`
|
|
152
|
+
Directory tree view. Shows structure of a path, optionally filtered by regex pattern.
|
|
153
|
+
|
|
154
|
+
Elements:
|
|
155
|
+
- \`<path>\` (required): Directory path to list (use \`.\` for repo root)
|
|
156
|
+
- \`<pattern>\` (optional): Regex to filter results
|
|
144
157
|
|
|
145
158
|
Examples:
|
|
146
159
|
\`\`\`
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
160
|
+
<list_directory>
|
|
161
|
+
<path>src/services</path>
|
|
162
|
+
</list_directory>
|
|
163
|
+
|
|
164
|
+
<list_directory>
|
|
165
|
+
<path>lib/utils</path>
|
|
166
|
+
<pattern>.*\\.(ts|js)$</pattern>
|
|
167
|
+
</list_directory>
|
|
151
168
|
\`\`\`
|
|
152
169
|
|
|
153
|
-
### \`read
|
|
154
|
-
Read file contents.
|
|
170
|
+
### \`read\`
|
|
171
|
+
Read file contents. Supports multiple line ranges.
|
|
155
172
|
- Returns numbered lines for easy reference
|
|
156
|
-
-
|
|
173
|
+
- ALWAYS include import statements (usually lines 1-20). Better to over-include than miss context.
|
|
174
|
+
|
|
175
|
+
Elements:
|
|
176
|
+
- \`<path>\` (required): File path to read
|
|
177
|
+
- \`<lines>\` (optional): Line ranges like "1-50,75-80,100-120" (omit to read entire file)
|
|
157
178
|
|
|
158
179
|
Examples:
|
|
159
180
|
\`\`\`
|
|
160
|
-
read
|
|
161
|
-
|
|
162
|
-
read
|
|
181
|
+
<read>
|
|
182
|
+
<path>src/main.py</path>
|
|
183
|
+
</read>
|
|
184
|
+
|
|
185
|
+
<read>
|
|
186
|
+
<path>src/auth.py</path>
|
|
187
|
+
<lines>1-20,45-80,150-200</lines>
|
|
188
|
+
</read>
|
|
163
189
|
\`\`\`
|
|
164
190
|
|
|
165
|
-
### \`grep
|
|
166
|
-
|
|
167
|
-
-
|
|
168
|
-
-
|
|
191
|
+
### \`grep\`
|
|
192
|
+
Search for pattern matches across files. Returns matches with 1 line of context above and below.
|
|
193
|
+
- Match lines use \`:\` separator \u2192 \`filepath:linenum:content\`
|
|
194
|
+
- Context lines use \`-\` separator \u2192 \`filepath-linenum-content\`
|
|
195
|
+
|
|
196
|
+
Elements:
|
|
197
|
+
- \`<pattern>\` (required): Search pattern (regex). Use \`(a|b)\` for OR patterns.
|
|
198
|
+
- \`<sub_dir>\` (optional): Subdirectory to search in (defaults to \`.\`)
|
|
199
|
+
- \`<glob>\` (optional): File pattern filter like \`*.py\` or \`*.{ts,tsx}\`
|
|
169
200
|
|
|
170
201
|
Examples:
|
|
171
202
|
\`\`\`
|
|
172
|
-
grep
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
grep
|
|
203
|
+
<grep>
|
|
204
|
+
<pattern>(authenticate|authorize|login)</pattern>
|
|
205
|
+
<sub_dir>src/auth/</sub_dir>
|
|
206
|
+
</grep>
|
|
207
|
+
|
|
208
|
+
<grep>
|
|
209
|
+
<pattern>class.*(Service|Controller)</pattern>
|
|
210
|
+
<glob>*.{ts,js}</glob>
|
|
211
|
+
</grep>
|
|
212
|
+
|
|
213
|
+
<grep>
|
|
214
|
+
<pattern>(DB_HOST|DATABASE_URL|connection)</pattern>
|
|
215
|
+
<glob>*.{py,yaml,env}</glob>
|
|
216
|
+
<sub_dir>lib/</sub_dir>
|
|
217
|
+
</grep>
|
|
176
218
|
\`\`\`
|
|
177
219
|
|
|
178
|
-
### \`finish
|
|
179
|
-
Submit final answer with all relevant code locations.
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
-
|
|
183
|
-
-
|
|
184
|
-
|
|
220
|
+
### \`finish\`
|
|
221
|
+
Submit final answer with all relevant code locations. Uses nested \`<file>\` elements.
|
|
222
|
+
|
|
223
|
+
File elements:
|
|
224
|
+
- \`<path>\` (required): File path
|
|
225
|
+
- \`<lines>\` (optional): Line ranges like "1-50,75-80" (\`*\` for entire file)
|
|
226
|
+
|
|
227
|
+
ALWAYS include import statements (usually lines 1-20). Better to over-include than miss context.
|
|
185
228
|
|
|
186
229
|
Examples:
|
|
187
230
|
\`\`\`
|
|
188
|
-
finish
|
|
189
|
-
|
|
231
|
+
<finish>
|
|
232
|
+
<file>
|
|
233
|
+
<path>src/auth.py</path>
|
|
234
|
+
<lines>1-15,25-50,75-80</lines>
|
|
235
|
+
</file>
|
|
236
|
+
<file>
|
|
237
|
+
<path>src/models/user.py</path>
|
|
238
|
+
<lines>*</lines>
|
|
239
|
+
</file>
|
|
240
|
+
</finish>
|
|
190
241
|
\`\`\`
|
|
191
242
|
</tools>
|
|
192
243
|
|
|
193
244
|
<strategy>
|
|
194
|
-
**Before your first tool call, classify the
|
|
245
|
+
**Before your first tool call, classify the search_string:**
|
|
195
246
|
|
|
196
|
-
|
|
|
197
|
-
|
|
198
|
-
| **Specific** (function name, error string, unique identifier) | 8 parallel greps on likely paths | Often by
|
|
199
|
-
| **Conceptual** (how does X work, where is Y handled) |
|
|
200
|
-
| **Exploratory** (find all tests, list API endpoints) |
|
|
247
|
+
| Search_string Type | Round 1 Strategy | Early Finish? |
|
|
248
|
+
|------------|------------------|---------------|
|
|
249
|
+
| **Specific** (function name, error string, unique identifier) | 8 parallel greps on likely paths | Often by round 2 |
|
|
250
|
+
| **Conceptual** (how does X work, where is Y handled) | list_directory + 2-3 broad greps | Rarely early |
|
|
251
|
+
| **Exploratory** (find all tests, list API endpoints) | list_directory at multiple depths | Usually needs 3 rounds |
|
|
201
252
|
|
|
202
253
|
**Parallel call patterns:**
|
|
203
254
|
- **Shotgun grep**: Same pattern, 8 different directories\u2014fast coverage
|
|
204
255
|
- **Variant grep**: 8 pattern variations (synonyms, naming conventions)\u2014catches inconsistent codebases
|
|
205
|
-
- **Funnel**: 1
|
|
256
|
+
- **Funnel**: 1 list_directory + 7 greps\u2014orient and search simultaneously
|
|
206
257
|
- **Deep read**: 8 reads on files you already identified\u2014gather full context fast
|
|
258
|
+
|
|
259
|
+
**Tool call expectations:**
|
|
260
|
+
- 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.
|
|
261
|
+
- 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.
|
|
262
|
+
- 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.
|
|
263
|
+
- 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.
|
|
207
264
|
</strategy>
|
|
208
265
|
|
|
209
266
|
<output_format>
|
|
210
267
|
EVERY response MUST follow this exact format:
|
|
211
268
|
|
|
212
269
|
1. First, wrap your reasoning in \`<think>...</think>\` tags containing:
|
|
213
|
-
-
|
|
214
|
-
- Confidence estimate (can I finish in 1-2
|
|
215
|
-
- This
|
|
270
|
+
- Search_string classification (specific/conceptual/exploratory)
|
|
271
|
+
- Confidence estimate (can I finish in 1-2 rounds?)
|
|
272
|
+
- This round's parallel strategy
|
|
216
273
|
- What signals would let me finish early?
|
|
217
274
|
|
|
218
|
-
2. Then, output
|
|
275
|
+
2. Then, output up to 8 tool calls using nested XML elements.
|
|
219
276
|
|
|
220
277
|
Example:
|
|
221
278
|
\`\`\`
|
|
222
279
|
<think>
|
|
223
|
-
This is a specific
|
|
224
|
-
High confidence I can finish in 2
|
|
280
|
+
This is a specific search_string about authentication. I'll grep for auth-related patterns.
|
|
281
|
+
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
|
|
225
282
|
Strategy: Shotgun grep across likely directories.
|
|
226
283
|
</think>
|
|
227
|
-
<
|
|
228
|
-
<
|
|
229
|
-
<
|
|
284
|
+
<grep>
|
|
285
|
+
<pattern>(authenticate|login|session)</pattern>
|
|
286
|
+
<sub_dir>src/auth/</sub_dir>
|
|
287
|
+
</grep>
|
|
288
|
+
<grep>
|
|
289
|
+
<pattern>(middleware|interceptor)</pattern>
|
|
290
|
+
<glob>*.{ts,js}</glob>
|
|
291
|
+
</grep>
|
|
292
|
+
<list_directory>
|
|
293
|
+
<path>src/auth</path>
|
|
294
|
+
</list_directory>
|
|
295
|
+
\`\`\`
|
|
296
|
+
|
|
297
|
+
Finishing example:
|
|
298
|
+
\`\`\`
|
|
299
|
+
<think>
|
|
300
|
+
I think I have a rough idea, but this is my last turn so I must call the finish tool regardless.
|
|
301
|
+
</think>
|
|
302
|
+
<finish>
|
|
303
|
+
<file>
|
|
304
|
+
<path>src/auth/login.py</path>
|
|
305
|
+
<lines>1-50</lines>
|
|
306
|
+
</file>
|
|
307
|
+
<file>
|
|
308
|
+
<path>src/middleware/session.py</path>
|
|
309
|
+
<lines>10-80</lines>
|
|
310
|
+
</file>
|
|
311
|
+
</finish>
|
|
230
312
|
\`\`\`
|
|
231
313
|
|
|
232
314
|
No commentary outside \`<think>\`. No explanations after tool calls.
|
|
@@ -239,17 +321,111 @@ When calling \`finish\`:
|
|
|
239
321
|
- Include any type definitions, interfaces, or constants used
|
|
240
322
|
- Better to over-include than leave the user missing context
|
|
241
323
|
- If unsure about boundaries, include more rather than less
|
|
242
|
-
</finishing_requirements
|
|
243
|
-
|
|
244
|
-
Begin your exploration now to find code relevant to the query.`;
|
|
324
|
+
</finishing_requirements>`;
|
|
245
325
|
function getSystemPrompt() {
|
|
246
326
|
return SYSTEM_PROMPT;
|
|
247
327
|
}
|
|
248
328
|
|
|
249
329
|
// tools/warp_grep/agent/parser.ts
|
|
250
|
-
var VALID_COMMANDS = ["
|
|
330
|
+
var VALID_COMMANDS = ["list_directory", "grep", "read", "finish"];
|
|
331
|
+
function isValidCommand(name) {
|
|
332
|
+
return VALID_COMMANDS.includes(name);
|
|
333
|
+
}
|
|
334
|
+
function getXmlElementText(xml, tagName) {
|
|
335
|
+
const regex = new RegExp(`<${tagName}>([\\s\\S]*?)</${tagName}>`, "i");
|
|
336
|
+
const match = xml.match(regex);
|
|
337
|
+
return match ? match[1].trim() : null;
|
|
338
|
+
}
|
|
339
|
+
function parseNestedXmlTools(text) {
|
|
340
|
+
const tools = [];
|
|
341
|
+
const toolRegex = /<([a-z_][a-z0-9_]*)>([\s\S]*?)<\/\1>/gi;
|
|
342
|
+
let match;
|
|
343
|
+
while ((match = toolRegex.exec(text)) !== null) {
|
|
344
|
+
const rawToolName = match[1].toLowerCase();
|
|
345
|
+
const content = match[2];
|
|
346
|
+
if (!isValidCommand(rawToolName)) continue;
|
|
347
|
+
const toolName = rawToolName;
|
|
348
|
+
if (toolName === "list_directory") {
|
|
349
|
+
const path3 = getXmlElementText(content, "path");
|
|
350
|
+
const pattern = getXmlElementText(content, "pattern");
|
|
351
|
+
if (path3) {
|
|
352
|
+
tools.push({ name: "list_directory", arguments: { path: path3, pattern } });
|
|
353
|
+
}
|
|
354
|
+
} else if (toolName === "grep") {
|
|
355
|
+
const pattern = getXmlElementText(content, "pattern");
|
|
356
|
+
const subDir = getXmlElementText(content, "sub_dir");
|
|
357
|
+
const glob = getXmlElementText(content, "glob");
|
|
358
|
+
if (pattern) {
|
|
359
|
+
tools.push({
|
|
360
|
+
name: "grep",
|
|
361
|
+
arguments: {
|
|
362
|
+
pattern,
|
|
363
|
+
path: subDir || ".",
|
|
364
|
+
...glob && { glob }
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
} else if (toolName === "read") {
|
|
369
|
+
const path3 = getXmlElementText(content, "path");
|
|
370
|
+
const linesStr = getXmlElementText(content, "lines");
|
|
371
|
+
if (path3) {
|
|
372
|
+
const args = { path: path3 };
|
|
373
|
+
if (linesStr) {
|
|
374
|
+
const ranges = [];
|
|
375
|
+
for (const rangeStr of linesStr.split(",")) {
|
|
376
|
+
const trimmed = rangeStr.trim();
|
|
377
|
+
if (!trimmed) continue;
|
|
378
|
+
const [s, e] = trimmed.split("-").map((v) => parseInt(v.trim(), 10));
|
|
379
|
+
if (Number.isFinite(s) && Number.isFinite(e)) {
|
|
380
|
+
ranges.push([s, e]);
|
|
381
|
+
} else if (Number.isFinite(s)) {
|
|
382
|
+
ranges.push([s, s]);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
if (ranges.length === 1) {
|
|
386
|
+
args.start = ranges[0][0];
|
|
387
|
+
args.end = ranges[0][1];
|
|
388
|
+
} else if (ranges.length > 1) {
|
|
389
|
+
args.lines = ranges;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
tools.push({ name: "read", arguments: args });
|
|
393
|
+
}
|
|
394
|
+
} else if (toolName === "finish") {
|
|
395
|
+
const fileRegex = /<file>([\s\S]*?)<\/file>/gi;
|
|
396
|
+
const files = [];
|
|
397
|
+
let fileMatch;
|
|
398
|
+
while ((fileMatch = fileRegex.exec(content)) !== null) {
|
|
399
|
+
const fileContent = fileMatch[1];
|
|
400
|
+
const filePath = getXmlElementText(fileContent, "path");
|
|
401
|
+
const linesStr = getXmlElementText(fileContent, "lines");
|
|
402
|
+
if (filePath && linesStr) {
|
|
403
|
+
const ranges = [];
|
|
404
|
+
for (const rangeStr of linesStr.split(",")) {
|
|
405
|
+
if (rangeStr.trim() === "*") {
|
|
406
|
+
ranges.push([1, 999999]);
|
|
407
|
+
} else {
|
|
408
|
+
const [s, e] = rangeStr.split("-").map((v) => parseInt(v.trim(), 10));
|
|
409
|
+
if (Number.isFinite(s) && Number.isFinite(e)) {
|
|
410
|
+
ranges.push([s, e]);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
if (ranges.length > 0) {
|
|
415
|
+
files.push({ path: filePath, lines: ranges });
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
if (files.length > 0) {
|
|
420
|
+
tools.push({ name: "finish", arguments: { files } });
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
return tools;
|
|
425
|
+
}
|
|
251
426
|
function preprocessText(text) {
|
|
252
427
|
let processed = text.replace(/<think>[\s\S]*?<\/think>/gi, "");
|
|
428
|
+
const nestedTools = parseNestedXmlTools(processed);
|
|
253
429
|
const openingTagRegex = /<tool_call>|<tool>/gi;
|
|
254
430
|
const closingTagRegex = /<\/tool_call>|<\/tool>/gi;
|
|
255
431
|
const openingMatches = processed.match(openingTagRegex) || [];
|
|
@@ -286,7 +462,7 @@ function preprocessText(text) {
|
|
|
286
462
|
}
|
|
287
463
|
}
|
|
288
464
|
}
|
|
289
|
-
return toolCallLines;
|
|
465
|
+
return { lines: toolCallLines, nestedTools };
|
|
290
466
|
}
|
|
291
467
|
var LLMResponseParser = class {
|
|
292
468
|
finishSpecSplitRe = /,(?=[^,\s]+:)/;
|
|
@@ -294,8 +470,8 @@ var LLMResponseParser = class {
|
|
|
294
470
|
if (typeof text !== "string") {
|
|
295
471
|
throw new TypeError("Command text must be a string.");
|
|
296
472
|
}
|
|
297
|
-
const lines = preprocessText(text);
|
|
298
|
-
const commands = [];
|
|
473
|
+
const { lines, nestedTools } = preprocessText(text);
|
|
474
|
+
const commands = [...nestedTools];
|
|
299
475
|
let finishAccumulator = null;
|
|
300
476
|
lines.forEach((line) => {
|
|
301
477
|
if (!line || line.startsWith("#")) return;
|
|
@@ -303,8 +479,8 @@ var LLMResponseParser = class {
|
|
|
303
479
|
if (parts.length === 0) return;
|
|
304
480
|
const cmd = parts[0];
|
|
305
481
|
switch (cmd) {
|
|
306
|
-
case "
|
|
307
|
-
this.
|
|
482
|
+
case "list_directory":
|
|
483
|
+
this.handleListDirectory(parts, line, commands);
|
|
308
484
|
break;
|
|
309
485
|
case "grep":
|
|
310
486
|
this.handleGrep(parts, line, commands);
|
|
@@ -322,8 +498,8 @@ var LLMResponseParser = class {
|
|
|
322
498
|
if (finishAccumulator) {
|
|
323
499
|
const map = finishAccumulator;
|
|
324
500
|
const entries = [...map.entries()];
|
|
325
|
-
const filesPayload = entries.map(([
|
|
326
|
-
path:
|
|
501
|
+
const filesPayload = entries.map(([path3, ranges]) => ({
|
|
502
|
+
path: path3,
|
|
327
503
|
lines: [...ranges].sort((a, b) => a[0] - b[0])
|
|
328
504
|
}));
|
|
329
505
|
commands.push({ name: "finish", arguments: { files: filesPayload } });
|
|
@@ -355,18 +531,17 @@ var LLMResponseParser = class {
|
|
|
355
531
|
skip(message) {
|
|
356
532
|
return { name: "_skip", arguments: { message } };
|
|
357
533
|
}
|
|
358
|
-
|
|
534
|
+
handleListDirectory(parts, rawLine, commands) {
|
|
359
535
|
if (parts.length < 2) {
|
|
360
536
|
commands.push(this.skip(
|
|
361
|
-
`[SKIPPED] Your command "${rawLine}" is missing a path. Correct format:
|
|
537
|
+
`[SKIPPED] Your command "${rawLine}" is missing a path. Correct format: list_directory <path> [pattern]. Example: list_directory src/`
|
|
362
538
|
));
|
|
363
539
|
return;
|
|
364
540
|
}
|
|
365
|
-
const
|
|
541
|
+
const path3 = parts[1];
|
|
366
542
|
const pattern = parts[2]?.replace(/^"|"$/g, "") ?? null;
|
|
367
|
-
commands.push({ name: "
|
|
543
|
+
commands.push({ name: "list_directory", arguments: { path: path3, pattern } });
|
|
368
544
|
}
|
|
369
|
-
// no glob tool in MCP
|
|
370
545
|
handleGrep(parts, rawLine, commands) {
|
|
371
546
|
if (parts.length < 3) {
|
|
372
547
|
commands.push(this.skip(
|
|
@@ -437,8 +612,30 @@ var LLMResponseParser = class {
|
|
|
437
612
|
}
|
|
438
613
|
};
|
|
439
614
|
|
|
440
|
-
// tools/warp_grep/tools/
|
|
615
|
+
// tools/warp_grep/agent/tools/grep.ts
|
|
616
|
+
async function toolGrep(provider, args) {
|
|
617
|
+
const res = await provider.grep(args);
|
|
618
|
+
if (res.error) {
|
|
619
|
+
return { output: res.error };
|
|
620
|
+
}
|
|
621
|
+
if (!res.lines.length) {
|
|
622
|
+
return { output: "no matches" };
|
|
623
|
+
}
|
|
624
|
+
return { output: res.lines.join("\n") };
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
// tools/warp_grep/agent/tools/read.ts
|
|
441
628
|
async function toolRead(provider, args) {
|
|
629
|
+
if (args.lines && args.lines.length > 0) {
|
|
630
|
+
const chunks = [];
|
|
631
|
+
for (const [start, end] of args.lines) {
|
|
632
|
+
const res2 = await provider.read({ path: args.path, start, end });
|
|
633
|
+
if (res2.error) return res2.error;
|
|
634
|
+
chunks.push(res2.lines.join("\n"));
|
|
635
|
+
}
|
|
636
|
+
if (chunks.every((c) => c === "")) return "(empty file)";
|
|
637
|
+
return chunks.join("\n...\n");
|
|
638
|
+
}
|
|
442
639
|
const res = await provider.read({ path: args.path, start: args.start, end: args.end });
|
|
443
640
|
if (res.error) {
|
|
444
641
|
return res.error;
|
|
@@ -447,16 +644,56 @@ async function toolRead(provider, args) {
|
|
|
447
644
|
return res.lines.join("\n");
|
|
448
645
|
}
|
|
449
646
|
|
|
450
|
-
// tools/warp_grep/tools/
|
|
451
|
-
async function
|
|
452
|
-
const list = await provider.
|
|
647
|
+
// tools/warp_grep/agent/tools/list_directory.ts
|
|
648
|
+
async function toolListDirectory(provider, args) {
|
|
649
|
+
const list = await provider.listDirectory({
|
|
453
650
|
path: args.path,
|
|
454
651
|
pattern: args.pattern ?? null,
|
|
455
|
-
maxResults: args.maxResults ??
|
|
456
|
-
maxDepth: args.maxDepth ??
|
|
652
|
+
maxResults: args.maxResults ?? AGENT_CONFIG.MAX_OUTPUT_LINES,
|
|
653
|
+
maxDepth: args.maxDepth ?? AGENT_CONFIG.MAX_LIST_DEPTH
|
|
457
654
|
});
|
|
458
655
|
if (!list.length) return "empty";
|
|
459
|
-
|
|
656
|
+
if (list.length >= AGENT_CONFIG.MAX_OUTPUT_LINES) {
|
|
657
|
+
return "query not specific enough, tool called tried to return too much context and failed";
|
|
658
|
+
}
|
|
659
|
+
return list.map((e) => {
|
|
660
|
+
const indent = " ".repeat(e.depth);
|
|
661
|
+
const name = e.type === "dir" ? `${e.name}/` : e.name;
|
|
662
|
+
return `${indent}${name}`;
|
|
663
|
+
}).join("\n");
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
// tools/warp_grep/agent/tools/finish.ts
|
|
667
|
+
async function readFinishFiles(repoRoot, files, reader) {
|
|
668
|
+
const out = [];
|
|
669
|
+
for (const f of files) {
|
|
670
|
+
const ranges = mergeRanges(f.lines);
|
|
671
|
+
const chunks = [];
|
|
672
|
+
for (const [s, e] of ranges) {
|
|
673
|
+
const lines = await reader(f.path, s, e);
|
|
674
|
+
chunks.push(lines.join("\n"));
|
|
675
|
+
}
|
|
676
|
+
out.push({ path: f.path, ranges, content: chunks.join("\n") });
|
|
677
|
+
}
|
|
678
|
+
return out;
|
|
679
|
+
}
|
|
680
|
+
function mergeRanges(ranges) {
|
|
681
|
+
if (!ranges.length) return [];
|
|
682
|
+
const sorted = [...ranges].sort((a, b) => a[0] - b[0]);
|
|
683
|
+
const merged = [];
|
|
684
|
+
let [cs, ce] = sorted[0];
|
|
685
|
+
for (let i = 1; i < sorted.length; i++) {
|
|
686
|
+
const [s, e] = sorted[i];
|
|
687
|
+
if (s <= ce + 1) {
|
|
688
|
+
ce = Math.max(ce, e);
|
|
689
|
+
} else {
|
|
690
|
+
merged.push([cs, ce]);
|
|
691
|
+
cs = s;
|
|
692
|
+
ce = e;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
merged.push([cs, ce]);
|
|
696
|
+
return merged;
|
|
460
697
|
}
|
|
461
698
|
|
|
462
699
|
// tools/utils/resilience.ts
|
|
@@ -549,8 +786,8 @@ var ToolOutputFormatter = class {
|
|
|
549
786
|
switch (name) {
|
|
550
787
|
case "read":
|
|
551
788
|
return this.formatRead(safeArgs, payload, isError);
|
|
552
|
-
case "
|
|
553
|
-
return this.
|
|
789
|
+
case "list_directory":
|
|
790
|
+
return this.formatListDirectory(safeArgs, payload, isError);
|
|
554
791
|
case "grep":
|
|
555
792
|
return this.formatGrep(safeArgs, payload, isError);
|
|
556
793
|
default:
|
|
@@ -563,39 +800,56 @@ ${payload}
|
|
|
563
800
|
if (isError) {
|
|
564
801
|
return payload;
|
|
565
802
|
}
|
|
566
|
-
const
|
|
567
|
-
|
|
803
|
+
const path3 = this.asString(args.path) || "...";
|
|
804
|
+
const start = args.start;
|
|
805
|
+
const end = args.end;
|
|
806
|
+
const linesArray = args.lines;
|
|
807
|
+
const attributes = [`path="${path3}"`];
|
|
808
|
+
if (linesArray && linesArray.length > 0) {
|
|
809
|
+
const rangeStr = linesArray.map(([s, e]) => `${s}-${e}`).join(",");
|
|
810
|
+
attributes.push(`lines="${rangeStr}"`);
|
|
811
|
+
} else if (start !== void 0 && end !== void 0) {
|
|
812
|
+
attributes.push(`lines="${start}-${end}"`);
|
|
813
|
+
}
|
|
814
|
+
return `<read ${attributes.join(" ")}>
|
|
568
815
|
${payload}
|
|
569
|
-
</
|
|
816
|
+
</read>`;
|
|
570
817
|
}
|
|
571
|
-
|
|
572
|
-
const
|
|
818
|
+
formatListDirectory(args, payload, isError) {
|
|
819
|
+
const path3 = this.asString(args.path) || ".";
|
|
820
|
+
const pattern = this.asString(args.pattern);
|
|
821
|
+
const attributes = [`path="${path3}"`];
|
|
822
|
+
if (pattern) {
|
|
823
|
+
attributes.push(`pattern="${pattern}"`);
|
|
824
|
+
}
|
|
573
825
|
if (isError) {
|
|
574
|
-
|
|
575
|
-
${payload}
|
|
576
|
-
</analyse_results>`;
|
|
826
|
+
attributes.push('status="error"');
|
|
577
827
|
}
|
|
578
|
-
return `<
|
|
828
|
+
return `<list_directory ${attributes.join(" ")}>
|
|
579
829
|
${payload}
|
|
580
|
-
</
|
|
830
|
+
</list_directory>`;
|
|
581
831
|
}
|
|
582
832
|
formatGrep(args, payload, isError) {
|
|
583
833
|
const pattern = this.asString(args.pattern);
|
|
584
|
-
const
|
|
834
|
+
const subDir = this.asString(args.path);
|
|
835
|
+
const glob = this.asString(args.glob);
|
|
585
836
|
const attributes = [];
|
|
586
837
|
if (pattern !== void 0) {
|
|
587
838
|
attributes.push(`pattern="${pattern}"`);
|
|
588
839
|
}
|
|
589
|
-
if (
|
|
590
|
-
attributes.push(`
|
|
840
|
+
if (subDir !== void 0) {
|
|
841
|
+
attributes.push(`sub_dir="${subDir}"`);
|
|
842
|
+
}
|
|
843
|
+
if (glob !== void 0) {
|
|
844
|
+
attributes.push(`glob="${glob}"`);
|
|
591
845
|
}
|
|
592
846
|
if (isError) {
|
|
593
847
|
attributes.push('status="error"');
|
|
594
848
|
}
|
|
595
849
|
const attrText = attributes.length ? ` ${attributes.join(" ")}` : "";
|
|
596
|
-
return `<
|
|
850
|
+
return `<grep${attrText}>
|
|
597
851
|
${payload}
|
|
598
|
-
</
|
|
852
|
+
</grep>`;
|
|
599
853
|
}
|
|
600
854
|
asString(value) {
|
|
601
855
|
if (value === null || value === void 0) {
|
|
@@ -609,66 +863,100 @@ function formatAgentToolOutput(toolName, args, output, options = {}) {
|
|
|
609
863
|
return sharedFormatter.format(toolName, args, output, options);
|
|
610
864
|
}
|
|
611
865
|
|
|
612
|
-
// tools/warp_grep/
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
chunks.push(lines.join("\n"));
|
|
621
|
-
}
|
|
622
|
-
out.push({ path: f.path, ranges, content: chunks.join("\n") });
|
|
866
|
+
// tools/warp_grep/agent/helpers.ts
|
|
867
|
+
var import_path = __toESM(require("path"), 1);
|
|
868
|
+
var TRUNCATED_MARKER = "[truncated for context limit]";
|
|
869
|
+
function formatTurnMessage(turnsUsed, maxTurns) {
|
|
870
|
+
const turnsRemaining = maxTurns - turnsUsed;
|
|
871
|
+
if (turnsRemaining === 1) {
|
|
872
|
+
return `
|
|
873
|
+
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`;
|
|
623
874
|
}
|
|
624
|
-
return
|
|
875
|
+
return `
|
|
876
|
+
You have used ${turnsUsed} turn${turnsUsed === 1 ? "" : "s"} and have ${turnsRemaining} remaining`;
|
|
625
877
|
}
|
|
626
|
-
function
|
|
627
|
-
|
|
628
|
-
const
|
|
629
|
-
const
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
if (s <= ce + 1) {
|
|
634
|
-
ce = Math.max(ce, e);
|
|
635
|
-
} else {
|
|
636
|
-
merged.push([cs, ce]);
|
|
637
|
-
cs = s;
|
|
638
|
-
ce = e;
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
merged.push([cs, ce]);
|
|
642
|
-
return merged;
|
|
878
|
+
function calculateContextBudget(messages) {
|
|
879
|
+
const totalChars = messages.reduce((sum, m) => sum + m.content.length, 0);
|
|
880
|
+
const maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS;
|
|
881
|
+
const percent = Math.round(totalChars / maxChars * 100);
|
|
882
|
+
const usedK = Math.round(totalChars / 1e3);
|
|
883
|
+
const maxK = Math.round(maxChars / 1e3);
|
|
884
|
+
return `<context_budget>${percent}% (${usedK}K/${maxK}K chars)</context_budget>`;
|
|
643
885
|
}
|
|
644
|
-
|
|
645
|
-
// tools/warp_grep/agent/runner.ts
|
|
646
|
-
var import_path = __toESM(require("path"), 1);
|
|
647
|
-
var parser = new LLMResponseParser();
|
|
648
886
|
async function buildInitialState(repoRoot, query, provider) {
|
|
649
887
|
try {
|
|
650
|
-
const entries = await provider.
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
888
|
+
const entries = await provider.listDirectory({
|
|
889
|
+
path: ".",
|
|
890
|
+
maxResults: AGENT_CONFIG.MAX_OUTPUT_LINES,
|
|
891
|
+
maxDepth: 2
|
|
892
|
+
});
|
|
893
|
+
const treeLines = entries.map((e) => {
|
|
894
|
+
const indent = " ".repeat(e.depth);
|
|
895
|
+
const name = e.type === "dir" ? `${e.name}/` : e.name;
|
|
896
|
+
return `${indent}${name}`;
|
|
897
|
+
});
|
|
898
|
+
const repoName = import_path.default.basename(repoRoot);
|
|
899
|
+
const treeOutput = treeLines.length > 0 ? `${repoName}/
|
|
900
|
+
${treeLines.join("\n")}` : `${repoName}/`;
|
|
901
|
+
return `<repo_structure>
|
|
902
|
+
${treeOutput}
|
|
903
|
+
</repo_structure>
|
|
904
|
+
|
|
905
|
+
<search_string>
|
|
906
|
+
${query}
|
|
907
|
+
</search_string>`;
|
|
659
908
|
} catch {
|
|
660
|
-
|
|
909
|
+
const repoName = import_path.default.basename(repoRoot);
|
|
910
|
+
return `<repo_structure>
|
|
911
|
+
${repoName}/
|
|
912
|
+
</repo_structure>
|
|
913
|
+
|
|
914
|
+
<search_string>
|
|
915
|
+
${query}
|
|
916
|
+
</search_string>`;
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
function enforceContextLimit(messages, maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS) {
|
|
920
|
+
const getTotalChars = () => messages.reduce((sum, m) => sum + m.content.length, 0);
|
|
921
|
+
if (getTotalChars() <= maxChars) {
|
|
922
|
+
return messages;
|
|
923
|
+
}
|
|
924
|
+
const userIndices = [];
|
|
925
|
+
let firstUserSkipped = false;
|
|
926
|
+
for (let i = 0; i < messages.length; i++) {
|
|
927
|
+
if (messages[i].role === "user") {
|
|
928
|
+
if (!firstUserSkipped) {
|
|
929
|
+
firstUserSkipped = true;
|
|
930
|
+
continue;
|
|
931
|
+
}
|
|
932
|
+
userIndices.push(i);
|
|
933
|
+
}
|
|
661
934
|
}
|
|
935
|
+
for (const idx of userIndices) {
|
|
936
|
+
if (getTotalChars() <= maxChars) {
|
|
937
|
+
break;
|
|
938
|
+
}
|
|
939
|
+
if (messages[idx].content !== TRUNCATED_MARKER) {
|
|
940
|
+
messages[idx] = { role: "user", content: TRUNCATED_MARKER };
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
return messages;
|
|
662
944
|
}
|
|
663
|
-
|
|
664
|
-
|
|
945
|
+
|
|
946
|
+
// tools/warp_grep/agent/runner.ts
|
|
947
|
+
var import_path2 = __toESM(require("path"), 1);
|
|
948
|
+
var parser = new LLMResponseParser();
|
|
949
|
+
var DEFAULT_API_URL = "https://api.morphllm.com";
|
|
950
|
+
async function callModel(messages, model, options = {}) {
|
|
951
|
+
const baseUrl = DEFAULT_API_URL;
|
|
952
|
+
const apiKey = options.morphApiKey || process.env.MORPH_API_KEY || "";
|
|
665
953
|
const fetchPromise = fetchWithRetry(
|
|
666
|
-
|
|
954
|
+
`${baseUrl}/v1/chat/completions`,
|
|
667
955
|
{
|
|
668
956
|
method: "POST",
|
|
669
957
|
headers: {
|
|
670
958
|
"Content-Type": "application/json",
|
|
671
|
-
Authorization: `Bearer ${apiKey
|
|
959
|
+
Authorization: `Bearer ${apiKey}`
|
|
672
960
|
},
|
|
673
961
|
body: JSON.stringify({
|
|
674
962
|
model,
|
|
@@ -677,10 +965,15 @@ async function callModel(messages, model, apiKey) {
|
|
|
677
965
|
messages
|
|
678
966
|
})
|
|
679
967
|
},
|
|
680
|
-
|
|
968
|
+
options.retryConfig
|
|
681
969
|
);
|
|
682
970
|
const resp = await withTimeout(fetchPromise, AGENT_CONFIG.TIMEOUT_MS, "morph-warp-grep request timed out");
|
|
683
971
|
if (!resp.ok) {
|
|
972
|
+
if (resp.status === 404) {
|
|
973
|
+
throw new Error(
|
|
974
|
+
"The endpoint you are trying to call is likely deprecated. Please update with: npm cache clean --force && npx -y @morphllm/morphmcp@latest or visit: https://morphllm.com/mcp"
|
|
975
|
+
);
|
|
976
|
+
}
|
|
684
977
|
const t = await resp.text();
|
|
685
978
|
throw new Error(`morph-warp-grep error ${resp.status}: ${t}`);
|
|
686
979
|
}
|
|
@@ -692,22 +985,24 @@ async function callModel(messages, model, apiKey) {
|
|
|
692
985
|
return content;
|
|
693
986
|
}
|
|
694
987
|
async function runWarpGrep(config) {
|
|
695
|
-
const repoRoot =
|
|
988
|
+
const repoRoot = import_path2.default.resolve(config.repoRoot || process.cwd());
|
|
696
989
|
const messages = [];
|
|
697
|
-
|
|
698
|
-
messages.push(systemMessage);
|
|
699
|
-
const queryContent = `<query>${config.query}</query>`;
|
|
700
|
-
messages.push({ role: "user", content: queryContent });
|
|
990
|
+
messages.push({ role: "system", content: getSystemPrompt() });
|
|
701
991
|
const initialState = await buildInitialState(repoRoot, config.query, config.provider);
|
|
702
992
|
messages.push({ role: "user", content: initialState });
|
|
703
|
-
const
|
|
993
|
+
const maxTurns = AGENT_CONFIG.MAX_TURNS;
|
|
704
994
|
const model = config.model || DEFAULT_MODEL;
|
|
705
995
|
const provider = config.provider;
|
|
706
996
|
const errors = [];
|
|
707
997
|
let finishMeta;
|
|
708
998
|
let terminationReason = "terminated";
|
|
709
|
-
for (let
|
|
710
|
-
|
|
999
|
+
for (let turn = 1; turn <= maxTurns; turn += 1) {
|
|
1000
|
+
enforceContextLimit(messages);
|
|
1001
|
+
const assistantContent = await callModel(messages, model, {
|
|
1002
|
+
morphApiKey: config.morphApiKey,
|
|
1003
|
+
morphApiUrl: config.morphApiUrl,
|
|
1004
|
+
retryConfig: config.retryConfig
|
|
1005
|
+
}).catch((e) => {
|
|
711
1006
|
errors.push({ message: e instanceof Error ? e.message : String(e) });
|
|
712
1007
|
return "";
|
|
713
1008
|
});
|
|
@@ -715,13 +1010,13 @@ async function runWarpGrep(config) {
|
|
|
715
1010
|
messages.push({ role: "assistant", content: assistantContent });
|
|
716
1011
|
const toolCalls = parser.parse(assistantContent);
|
|
717
1012
|
if (toolCalls.length === 0) {
|
|
718
|
-
errors.push({ message: "No tool calls produced by the model." });
|
|
1013
|
+
errors.push({ message: "No tool calls produced by the model. Your MCP is likely out of date! Update it by running: npm cache clean --force && npx -y @morphllm/morphmcp@latest" });
|
|
719
1014
|
terminationReason = "terminated";
|
|
720
1015
|
break;
|
|
721
1016
|
}
|
|
722
1017
|
const finishCalls = toolCalls.filter((c) => c.name === "finish");
|
|
723
1018
|
const grepCalls = toolCalls.filter((c) => c.name === "grep");
|
|
724
|
-
const
|
|
1019
|
+
const listDirCalls = toolCalls.filter((c) => c.name === "list_directory");
|
|
725
1020
|
const readCalls = toolCalls.filter((c) => c.name === "read");
|
|
726
1021
|
const skipCalls = toolCalls.filter((c) => c.name === "_skip");
|
|
727
1022
|
const formatted = [];
|
|
@@ -733,24 +1028,18 @@ async function runWarpGrep(config) {
|
|
|
733
1028
|
for (const c of grepCalls) {
|
|
734
1029
|
const args = c.arguments ?? {};
|
|
735
1030
|
allPromises.push(
|
|
736
|
-
provider
|
|
737
|
-
(
|
|
738
|
-
if (grepRes.error) {
|
|
739
|
-
return { terminate: true, error: grepRes.error };
|
|
740
|
-
}
|
|
741
|
-
const output = grepRes.lines.join("\n") || "no matches";
|
|
742
|
-
return formatAgentToolOutput("grep", args, output, { isError: false });
|
|
743
|
-
},
|
|
1031
|
+
toolGrep(provider, args).then(
|
|
1032
|
+
({ output }) => formatAgentToolOutput("grep", args, output, { isError: false }),
|
|
744
1033
|
(err) => formatAgentToolOutput("grep", args, String(err), { isError: true })
|
|
745
1034
|
)
|
|
746
1035
|
);
|
|
747
1036
|
}
|
|
748
|
-
for (const c of
|
|
1037
|
+
for (const c of listDirCalls) {
|
|
749
1038
|
const args = c.arguments ?? {};
|
|
750
1039
|
allPromises.push(
|
|
751
|
-
|
|
752
|
-
(p) => formatAgentToolOutput("
|
|
753
|
-
(err) => formatAgentToolOutput("
|
|
1040
|
+
toolListDirectory(provider, args).then(
|
|
1041
|
+
(p) => formatAgentToolOutput("list_directory", args, p, { isError: false }),
|
|
1042
|
+
(err) => formatAgentToolOutput("list_directory", args, String(err), { isError: true })
|
|
754
1043
|
)
|
|
755
1044
|
);
|
|
756
1045
|
}
|
|
@@ -765,34 +1054,12 @@ async function runWarpGrep(config) {
|
|
|
765
1054
|
}
|
|
766
1055
|
const allResults = await Promise.all(allPromises);
|
|
767
1056
|
for (const result of allResults) {
|
|
768
|
-
if (typeof result === "object" && "terminate" in result) {
|
|
769
|
-
errors.push({ message: result.error });
|
|
770
|
-
return {
|
|
771
|
-
terminationReason: "terminated",
|
|
772
|
-
messages,
|
|
773
|
-
errors
|
|
774
|
-
};
|
|
775
|
-
}
|
|
776
1057
|
formatted.push(result);
|
|
777
1058
|
}
|
|
778
1059
|
if (formatted.length > 0) {
|
|
779
|
-
const
|
|
780
|
-
const
|
|
781
|
-
|
|
782
|
-
if (turnsRemaining === 0) {
|
|
783
|
-
turnMessage = `
|
|
784
|
-
|
|
785
|
-
[Turn ${turnsUsed}/4] This is your LAST turn. You MUST call the finish tool now.`;
|
|
786
|
-
} else if (turnsRemaining === 1) {
|
|
787
|
-
turnMessage = `
|
|
788
|
-
|
|
789
|
-
[Turn ${turnsUsed}/4] You have 1 turn remaining. Next turn you MUST call the finish tool.`;
|
|
790
|
-
} else {
|
|
791
|
-
turnMessage = `
|
|
792
|
-
|
|
793
|
-
[Turn ${turnsUsed}/4] You have ${turnsRemaining} turns remaining.`;
|
|
794
|
-
}
|
|
795
|
-
messages.push({ role: "user", content: formatted.join("\n") + turnMessage });
|
|
1060
|
+
const turnMessage = formatTurnMessage(turn, maxTurns);
|
|
1061
|
+
const contextBudget = calculateContextBudget(messages);
|
|
1062
|
+
messages.push({ role: "user", content: formatted.join("\n") + turnMessage + "\n" + contextBudget });
|
|
796
1063
|
}
|
|
797
1064
|
if (finishCalls.length) {
|
|
798
1065
|
const fc = finishCalls[0];
|