@morphllm/morphsdk 0.2.143 → 0.2.145
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/{chunk-YIETFYCL.js → chunk-4LWMPKSB.js} +71 -44
- package/dist/chunk-4LWMPKSB.js.map +1 -0
- package/dist/{chunk-HZOTLGJH.js → chunk-4Y2NM6JD.js} +42 -2
- package/dist/chunk-4Y2NM6JD.js.map +1 -0
- package/dist/{chunk-YTPYQAIO.js → chunk-5FCXLQJU.js} +3 -3
- package/dist/{chunk-KQ6QSAE7.js → chunk-6CFKWZK3.js} +3 -3
- package/dist/{chunk-NF2QWJDY.js → chunk-B3AKP3RA.js} +31 -2
- package/dist/chunk-B3AKP3RA.js.map +1 -0
- package/dist/{chunk-ALY5S4XC.js → chunk-BAF33L6C.js} +2 -2
- package/dist/{chunk-TRPJAKAS.js → chunk-BXRJYLRS.js} +2 -2
- package/dist/chunk-CMSHXALI.js +60 -0
- package/dist/chunk-CMSHXALI.js.map +1 -0
- package/dist/{chunk-X5IVORU2.js → chunk-EPIOAODF.js} +2 -2
- package/dist/{chunk-T53IMLYK.js → chunk-G23BI5CQ.js} +2 -2
- package/dist/{chunk-FZQZZP3R.js → chunk-GHPQYSSF.js} +2 -2
- package/dist/{chunk-7POOJWBR.js → chunk-GXCWKYGU.js} +2 -2
- package/dist/{chunk-PBOKDQR3.js → chunk-GXM3G7Z4.js} +3 -3
- package/dist/{chunk-OSQ2EMUP.js → chunk-HBIW2XV2.js} +2 -2
- package/dist/{chunk-FJVYE6ZX.js → chunk-HE7K2QNQ.js} +17 -17
- package/dist/{chunk-FIVYDIHX.js → chunk-HYRHI2UL.js} +1 -1
- package/dist/{chunk-DKODF3YG.js → chunk-I3J46TSB.js} +5 -4
- package/dist/chunk-I3J46TSB.js.map +1 -0
- package/dist/{chunk-F6JGAEK5.js → chunk-IRWHN55G.js} +1 -1
- package/dist/{chunk-QFCED636.js → chunk-JHYH3NEP.js} +2 -2
- package/dist/chunk-JHYH3NEP.js.map +1 -0
- package/dist/{chunk-YJG2KRXY.js → chunk-JMUAQQJU.js} +3 -3
- package/dist/{chunk-W27OOF55.js → chunk-JRBU4UNP.js} +2 -2
- package/dist/{chunk-DN3WS37U.js → chunk-KELRCMA6.js} +2 -2
- package/dist/{chunk-DN3WS37U.js.map → chunk-KELRCMA6.js.map} +1 -1
- package/dist/{chunk-TLPFEK5S.js → chunk-MRPASJBX.js} +2 -2
- package/dist/{chunk-RMBIT3I3.js → chunk-MTJ3PR4M.js} +2 -2
- package/dist/{chunk-VFGM343D.js → chunk-N7TTZIBK.js} +2 -2
- package/dist/chunk-OPEQQGST.js +396 -0
- package/dist/chunk-OPEQQGST.js.map +1 -0
- package/dist/{chunk-Z5APBTQP.js → chunk-PX7ODEML.js} +2 -2
- package/dist/{chunk-OOZZE5BQ.js → chunk-RZXS4ADX.js} +2 -2
- package/dist/{chunk-VBSO32I2.js → chunk-S54SPKX3.js} +3 -3
- package/dist/{chunk-ENLZ45ZZ.js → chunk-SUE4GYA2.js} +2 -2
- package/dist/{chunk-TXOUDREW.js → chunk-VRV5UYTN.js} +2 -2
- package/dist/{chunk-ZGJLA2O6.js → chunk-XL7R3XN5.js} +2 -2
- package/dist/client.cjs +427 -439
- package/dist/client.cjs.map +1 -1
- package/dist/client.js +27 -28
- package/dist/edge.cjs +1 -1
- package/dist/edge.cjs.map +1 -1
- package/dist/edge.js +4 -4
- package/dist/{finish-DBKuo8yj.d.ts → finish-Ddj1MPGt.d.ts} +1 -1
- package/dist/index.cjs +446 -439
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +30 -30
- package/dist/modelrouter/core.cjs +1 -1
- package/dist/modelrouter/core.cjs.map +1 -1
- package/dist/modelrouter/core.js +3 -3
- package/dist/modelrouter/index.cjs +1 -1
- package/dist/modelrouter/index.cjs.map +1 -1
- package/dist/modelrouter/index.js +3 -3
- package/dist/subagents/anthropic.cjs +422 -434
- package/dist/subagents/anthropic.cjs.map +1 -1
- package/dist/subagents/anthropic.js +8 -9
- package/dist/subagents/vercel.cjs +422 -434
- package/dist/subagents/vercel.cjs.map +1 -1
- package/dist/subagents/vercel.js +8 -9
- package/dist/tools/browser/anthropic.cjs +1 -1
- package/dist/tools/browser/anthropic.cjs.map +1 -1
- package/dist/tools/browser/anthropic.js +5 -5
- package/dist/tools/browser/core.cjs +1 -1
- package/dist/tools/browser/core.cjs.map +1 -1
- package/dist/tools/browser/core.js +4 -4
- package/dist/tools/browser/index.cjs +1 -1
- package/dist/tools/browser/index.cjs.map +1 -1
- package/dist/tools/browser/index.js +7 -7
- package/dist/tools/browser/openai.cjs +1 -1
- package/dist/tools/browser/openai.cjs.map +1 -1
- package/dist/tools/browser/openai.js +5 -5
- package/dist/tools/browser/profiles/core.cjs +1 -1
- package/dist/tools/browser/profiles/core.cjs.map +1 -1
- package/dist/tools/browser/profiles/core.js +3 -3
- package/dist/tools/browser/profiles/index.cjs +1 -1
- package/dist/tools/browser/profiles/index.cjs.map +1 -1
- package/dist/tools/browser/profiles/index.js +3 -3
- package/dist/tools/browser/vercel.cjs +1 -1
- package/dist/tools/browser/vercel.cjs.map +1 -1
- package/dist/tools/browser/vercel.js +5 -5
- package/dist/tools/codebase_search/anthropic.cjs +1 -1
- package/dist/tools/codebase_search/anthropic.cjs.map +1 -1
- package/dist/tools/codebase_search/anthropic.js +4 -4
- package/dist/tools/codebase_search/core.cjs +1 -1
- package/dist/tools/codebase_search/core.cjs.map +1 -1
- package/dist/tools/codebase_search/core.js +3 -3
- package/dist/tools/codebase_search/index.cjs +1 -1
- package/dist/tools/codebase_search/index.cjs.map +1 -1
- package/dist/tools/codebase_search/index.js +6 -6
- package/dist/tools/codebase_search/openai.cjs +1 -1
- package/dist/tools/codebase_search/openai.cjs.map +1 -1
- package/dist/tools/codebase_search/openai.js +4 -4
- package/dist/tools/codebase_search/vercel.cjs +1 -1
- package/dist/tools/codebase_search/vercel.cjs.map +1 -1
- package/dist/tools/codebase_search/vercel.js +4 -4
- package/dist/tools/fastapply/anthropic.cjs +1 -1
- package/dist/tools/fastapply/anthropic.cjs.map +1 -1
- package/dist/tools/fastapply/anthropic.js +4 -4
- package/dist/tools/fastapply/apply.cjs +1 -1
- package/dist/tools/fastapply/apply.cjs.map +1 -1
- package/dist/tools/fastapply/apply.js +2 -2
- package/dist/tools/fastapply/core.cjs +1 -1
- package/dist/tools/fastapply/core.cjs.map +1 -1
- package/dist/tools/fastapply/core.js +3 -3
- package/dist/tools/fastapply/index.cjs +1 -1
- package/dist/tools/fastapply/index.cjs.map +1 -1
- package/dist/tools/fastapply/index.js +6 -6
- package/dist/tools/fastapply/openai.cjs +1 -1
- package/dist/tools/fastapply/openai.cjs.map +1 -1
- package/dist/tools/fastapply/openai.js +4 -4
- package/dist/tools/fastapply/vercel.cjs +1 -1
- package/dist/tools/fastapply/vercel.cjs.map +1 -1
- package/dist/tools/fastapply/vercel.js +4 -4
- package/dist/tools/index.cjs +1 -1
- package/dist/tools/index.cjs.map +1 -1
- package/dist/tools/index.js +6 -6
- package/dist/tools/utils/resilience.cjs +1 -1
- package/dist/tools/utils/resilience.cjs.map +1 -1
- package/dist/tools/utils/resilience.js +2 -2
- package/dist/tools/warp_grep/agent/config.cjs +4 -3
- package/dist/tools/warp_grep/agent/config.cjs.map +1 -1
- package/dist/tools/warp_grep/agent/config.d.ts +2 -1
- package/dist/tools/warp_grep/agent/config.js +1 -1
- package/dist/tools/warp_grep/agent/parser.cjs +52 -121
- package/dist/tools/warp_grep/agent/parser.cjs.map +1 -1
- package/dist/tools/warp_grep/agent/parser.d.ts +12 -5
- package/dist/tools/warp_grep/agent/parser.js +7 -3
- package/dist/tools/warp_grep/agent/runner.cjs +335 -416
- package/dist/tools/warp_grep/agent/runner.cjs.map +1 -1
- package/dist/tools/warp_grep/agent/runner.d.ts +6 -3
- package/dist/tools/warp_grep/agent/runner.js +5 -6
- package/dist/tools/warp_grep/agent/types.cjs.map +1 -1
- package/dist/tools/warp_grep/agent/types.d.ts +22 -3
- package/dist/tools/warp_grep/anthropic.cjs +423 -435
- package/dist/tools/warp_grep/anthropic.cjs.map +1 -1
- package/dist/tools/warp_grep/anthropic.js +9 -10
- package/dist/tools/warp_grep/client.cjs +422 -434
- package/dist/tools/warp_grep/client.cjs.map +1 -1
- package/dist/tools/warp_grep/client.js +7 -8
- package/dist/tools/warp_grep/gemini.cjs +423 -435
- package/dist/tools/warp_grep/gemini.cjs.map +1 -1
- package/dist/tools/warp_grep/gemini.js +8 -9
- package/dist/tools/warp_grep/gemini.js.map +1 -1
- package/dist/tools/warp_grep/harness.cjs +164 -176
- package/dist/tools/warp_grep/harness.cjs.map +1 -1
- package/dist/tools/warp_grep/harness.d.ts +17 -38
- package/dist/tools/warp_grep/harness.js +15 -14
- package/dist/tools/warp_grep/harness.js.map +1 -1
- package/dist/tools/warp_grep/index.cjs +442 -435
- package/dist/tools/warp_grep/index.cjs.map +1 -1
- package/dist/tools/warp_grep/index.d.ts +1 -1
- package/dist/tools/warp_grep/index.js +11 -11
- package/dist/tools/warp_grep/openai.cjs +423 -435
- package/dist/tools/warp_grep/openai.cjs.map +1 -1
- package/dist/tools/warp_grep/openai.js +9 -10
- package/dist/tools/warp_grep/providers/local.cjs +43 -2
- package/dist/tools/warp_grep/providers/local.cjs.map +1 -1
- package/dist/tools/warp_grep/providers/local.d.ts +5 -1
- package/dist/tools/warp_grep/providers/local.js +2 -2
- package/dist/tools/warp_grep/providers/remote.cjs +32 -2
- package/dist/tools/warp_grep/providers/remote.cjs.map +1 -1
- package/dist/tools/warp_grep/providers/remote.d.ts +9 -1
- package/dist/tools/warp_grep/providers/remote.js +2 -2
- package/dist/tools/warp_grep/providers/types.cjs.map +1 -1
- package/dist/tools/warp_grep/providers/types.d.ts +14 -1
- package/dist/tools/warp_grep/vercel.cjs +423 -435
- package/dist/tools/warp_grep/vercel.cjs.map +1 -1
- package/dist/tools/warp_grep/vercel.js +9 -10
- package/dist/version.cjs +1 -1
- package/dist/version.cjs.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-DKODF3YG.js.map +0 -1
- package/dist/chunk-HZOTLGJH.js.map +0 -1
- package/dist/chunk-NF2QWJDY.js.map +0 -1
- package/dist/chunk-QFCED636.js.map +0 -1
- package/dist/chunk-SSQYLUKI.js +0 -409
- package/dist/chunk-SSQYLUKI.js.map +0 -1
- package/dist/chunk-VCKJ22DX.js +0 -131
- package/dist/chunk-VCKJ22DX.js.map +0 -1
- package/dist/chunk-YIETFYCL.js.map +0 -1
- /package/dist/{chunk-YTPYQAIO.js.map → chunk-5FCXLQJU.js.map} +0 -0
- /package/dist/{chunk-KQ6QSAE7.js.map → chunk-6CFKWZK3.js.map} +0 -0
- /package/dist/{chunk-ALY5S4XC.js.map → chunk-BAF33L6C.js.map} +0 -0
- /package/dist/{chunk-TRPJAKAS.js.map → chunk-BXRJYLRS.js.map} +0 -0
- /package/dist/{chunk-X5IVORU2.js.map → chunk-EPIOAODF.js.map} +0 -0
- /package/dist/{chunk-T53IMLYK.js.map → chunk-G23BI5CQ.js.map} +0 -0
- /package/dist/{chunk-FZQZZP3R.js.map → chunk-GHPQYSSF.js.map} +0 -0
- /package/dist/{chunk-7POOJWBR.js.map → chunk-GXCWKYGU.js.map} +0 -0
- /package/dist/{chunk-PBOKDQR3.js.map → chunk-GXM3G7Z4.js.map} +0 -0
- /package/dist/{chunk-OSQ2EMUP.js.map → chunk-HBIW2XV2.js.map} +0 -0
- /package/dist/{chunk-FJVYE6ZX.js.map → chunk-HE7K2QNQ.js.map} +0 -0
- /package/dist/{chunk-FIVYDIHX.js.map → chunk-HYRHI2UL.js.map} +0 -0
- /package/dist/{chunk-F6JGAEK5.js.map → chunk-IRWHN55G.js.map} +0 -0
- /package/dist/{chunk-YJG2KRXY.js.map → chunk-JMUAQQJU.js.map} +0 -0
- /package/dist/{chunk-W27OOF55.js.map → chunk-JRBU4UNP.js.map} +0 -0
- /package/dist/{chunk-TLPFEK5S.js.map → chunk-MRPASJBX.js.map} +0 -0
- /package/dist/{chunk-RMBIT3I3.js.map → chunk-MTJ3PR4M.js.map} +0 -0
- /package/dist/{chunk-VFGM343D.js.map → chunk-N7TTZIBK.js.map} +0 -0
- /package/dist/{chunk-Z5APBTQP.js.map → chunk-PX7ODEML.js.map} +0 -0
- /package/dist/{chunk-OOZZE5BQ.js.map → chunk-RZXS4ADX.js.map} +0 -0
- /package/dist/{chunk-VBSO32I2.js.map → chunk-S54SPKX3.js.map} +0 -0
- /package/dist/{chunk-ENLZ45ZZ.js.map → chunk-SUE4GYA2.js.map} +0 -0
- /package/dist/{chunk-TXOUDREW.js.map → chunk-VRV5UYTN.js.map} +0 -0
- /package/dist/{chunk-ZGJLA2O6.js.map → chunk-XL7R3XN5.js.map} +0 -0
|
@@ -41,11 +41,12 @@ var init_config = __esm({
|
|
|
41
41
|
return isNaN(parsed) || parsed <= 0 ? defaultMs : parsed;
|
|
42
42
|
};
|
|
43
43
|
AGENT_CONFIG = {
|
|
44
|
-
MAX_TURNS:
|
|
44
|
+
MAX_TURNS: 6,
|
|
45
45
|
/** Default timeout for model calls. Can be overridden via MORPH_WARP_GREP_TIMEOUT env var (in ms) */
|
|
46
46
|
TIMEOUT_MS: parseEnvTimeout(process.env.MORPH_WARP_GREP_TIMEOUT, 3e4),
|
|
47
|
-
MAX_CONTEXT_CHARS:
|
|
47
|
+
MAX_CONTEXT_CHARS: 321600,
|
|
48
48
|
MAX_OUTPUT_LINES: 200,
|
|
49
|
+
MAX_LIST_RESULTS: 500,
|
|
49
50
|
MAX_READ_LINES: 800,
|
|
50
51
|
MAX_LIST_DEPTH: 3,
|
|
51
52
|
LIST_TIMEOUT_MS: 2e3
|
|
@@ -130,7 +131,7 @@ var init_config = __esm({
|
|
|
130
131
|
".*"
|
|
131
132
|
];
|
|
132
133
|
DEFAULT_EXCLUDES = (process.env.MORPH_WARP_GREP_EXCLUDE || "").split(",").map((s) => s.trim()).filter(Boolean).concat(BUILTIN_EXCLUDES);
|
|
133
|
-
DEFAULT_MODEL = "morph-warp-grep-v2";
|
|
134
|
+
DEFAULT_MODEL = "morph-warp-grep-v2.1";
|
|
134
135
|
}
|
|
135
136
|
});
|
|
136
137
|
|
|
@@ -203,19 +204,19 @@ var init_ripgrep = __esm({
|
|
|
203
204
|
|
|
204
205
|
// tools/warp_grep/utils/paths.ts
|
|
205
206
|
function resolveUnderRepo(repoRoot, targetPath) {
|
|
206
|
-
const absRoot =
|
|
207
|
-
const resolved =
|
|
207
|
+
const absRoot = import_path4.default.resolve(repoRoot);
|
|
208
|
+
const resolved = import_path4.default.resolve(absRoot, targetPath);
|
|
208
209
|
ensureWithinRepo(absRoot, resolved);
|
|
209
210
|
return resolved;
|
|
210
211
|
}
|
|
211
212
|
function ensureWithinRepo(repoRoot, absTarget) {
|
|
212
|
-
const rel =
|
|
213
|
-
if (rel.startsWith("..") ||
|
|
213
|
+
const rel = import_path4.default.relative(import_path4.default.resolve(repoRoot), import_path4.default.resolve(absTarget));
|
|
214
|
+
if (rel.startsWith("..") || import_path4.default.isAbsolute(rel)) {
|
|
214
215
|
throw new Error(`Path outside repository root: ${absTarget}`);
|
|
215
216
|
}
|
|
216
217
|
}
|
|
217
218
|
function toRepoRelative(repoRoot, absPath) {
|
|
218
|
-
return
|
|
219
|
+
return import_path4.default.relative(import_path4.default.resolve(repoRoot), import_path4.default.resolve(absPath));
|
|
219
220
|
}
|
|
220
221
|
function isSymlink(p) {
|
|
221
222
|
try {
|
|
@@ -226,7 +227,7 @@ function isSymlink(p) {
|
|
|
226
227
|
}
|
|
227
228
|
}
|
|
228
229
|
function fixPathRepetition(fullPath) {
|
|
229
|
-
const segments = fullPath.split(
|
|
230
|
+
const segments = fullPath.split(import_path4.default.sep).filter(Boolean);
|
|
230
231
|
if (segments.length < 2) return null;
|
|
231
232
|
for (let len = Math.floor(segments.length / 2); len >= 1; len--) {
|
|
232
233
|
for (let i = 0; i <= segments.length - 2 * len; i++) {
|
|
@@ -234,7 +235,7 @@ function fixPathRepetition(fullPath) {
|
|
|
234
235
|
const second = segments.slice(i + len, i + 2 * len);
|
|
235
236
|
if (first.every((seg, idx) => seg === second[idx])) {
|
|
236
237
|
const fixed = [...segments.slice(0, i), ...segments.slice(i + len)];
|
|
237
|
-
return
|
|
238
|
+
return import_path4.default.sep + fixed.join(import_path4.default.sep);
|
|
238
239
|
}
|
|
239
240
|
}
|
|
240
241
|
}
|
|
@@ -258,12 +259,12 @@ function isTextualFile(filePath, maxBytes = 2e6) {
|
|
|
258
259
|
return false;
|
|
259
260
|
}
|
|
260
261
|
}
|
|
261
|
-
var import_fs,
|
|
262
|
+
var import_fs, import_path4;
|
|
262
263
|
var init_paths = __esm({
|
|
263
264
|
"tools/warp_grep/utils/paths.ts"() {
|
|
264
265
|
"use strict";
|
|
265
266
|
import_fs = __toESM(require("fs"), 1);
|
|
266
|
-
|
|
267
|
+
import_path4 = __toESM(require("path"), 1);
|
|
267
268
|
}
|
|
268
269
|
});
|
|
269
270
|
|
|
@@ -294,12 +295,12 @@ function shouldSkip2(name, allowNames) {
|
|
|
294
295
|
}
|
|
295
296
|
return false;
|
|
296
297
|
}
|
|
297
|
-
var import_promises2,
|
|
298
|
+
var import_promises2, import_path5, SKIP_NAMES2, SKIP_EXTENSIONS2, LocalRipgrepProvider;
|
|
298
299
|
var init_local = __esm({
|
|
299
300
|
"tools/warp_grep/providers/local.ts"() {
|
|
300
301
|
"use strict";
|
|
301
302
|
import_promises2 = __toESM(require("fs/promises"), 1);
|
|
302
|
-
|
|
303
|
+
import_path5 = __toESM(require("path"), 1);
|
|
303
304
|
init_ripgrep();
|
|
304
305
|
init_paths();
|
|
305
306
|
init_files();
|
|
@@ -401,7 +402,7 @@ var init_local = __esm({
|
|
|
401
402
|
}
|
|
402
403
|
const stat = await import_promises2.default.stat(abs).catch(() => null);
|
|
403
404
|
if (!stat) return { lines: [] };
|
|
404
|
-
const targetArg = abs ===
|
|
405
|
+
const targetArg = abs === import_path5.default.resolve(this.repoRoot) ? "." : toRepoRelative(this.repoRoot, abs);
|
|
405
406
|
const contextLines = params.context_lines !== void 0 ? String(params.context_lines) : "1";
|
|
406
407
|
const args = [
|
|
407
408
|
"--no-config",
|
|
@@ -556,7 +557,7 @@ Details: ${res.stderr}` : ""}`
|
|
|
556
557
|
if (timedOut || results.length >= maxResults) break;
|
|
557
558
|
if (shouldSkip2(entry.name, allowNames)) continue;
|
|
558
559
|
if (regex && !regex.test(entry.name)) continue;
|
|
559
|
-
const full =
|
|
560
|
+
const full = import_path5.default.join(dir, entry.name);
|
|
560
561
|
const isDir = entry.isDirectory();
|
|
561
562
|
results.push({
|
|
562
563
|
name: entry.name,
|
|
@@ -572,6 +573,46 @@ Details: ${res.stderr}` : ""}`
|
|
|
572
573
|
await walk(abs, 0);
|
|
573
574
|
return results;
|
|
574
575
|
}
|
|
576
|
+
async glob(params) {
|
|
577
|
+
let abs;
|
|
578
|
+
try {
|
|
579
|
+
abs = params.path ? resolveUnderRepo(this.repoRoot, params.path) : this.repoRoot;
|
|
580
|
+
} catch (err) {
|
|
581
|
+
return { files: [], searchDir: this.repoRoot, totalFound: 0, error: `[PATH ERROR] ${err instanceof Error ? err.message : String(err)}` };
|
|
582
|
+
}
|
|
583
|
+
const stat = await import_promises2.default.stat(abs).catch(() => null);
|
|
584
|
+
if (!stat || !stat.isDirectory()) {
|
|
585
|
+
return { files: [], searchDir: abs, totalFound: 0, error: `[PATH ERROR] Directory not found: ${params.path || "."}` };
|
|
586
|
+
}
|
|
587
|
+
const targetArg = abs === import_path5.default.resolve(this.repoRoot) ? "." : toRepoRelative(this.repoRoot, abs);
|
|
588
|
+
const args = [
|
|
589
|
+
"--no-config",
|
|
590
|
+
"--files",
|
|
591
|
+
"--color=never",
|
|
592
|
+
"-g",
|
|
593
|
+
params.pattern,
|
|
594
|
+
...this.excludes.filter((e) => !this.allowNames?.has(e)).flatMap((e) => ["-g", `!${e}`]),
|
|
595
|
+
targetArg || "."
|
|
596
|
+
];
|
|
597
|
+
const res = await runRipgrep(args, { cwd: this.repoRoot });
|
|
598
|
+
if (res.exitCode === -1) {
|
|
599
|
+
return { files: [], searchDir: abs, totalFound: 0, error: `[RIPGREP NOT AVAILABLE] ripgrep (rg) is required for glob search.` };
|
|
600
|
+
}
|
|
601
|
+
if (res.exitCode !== 0 && res.exitCode !== 1) {
|
|
602
|
+
return { files: [], searchDir: abs, totalFound: 0, error: `[GLOB ERROR] glob failed with exit code ${res.exitCode}${res.stderr ? `: ${res.stderr}` : ""}` };
|
|
603
|
+
}
|
|
604
|
+
const absRoot = import_path5.default.resolve(this.repoRoot);
|
|
605
|
+
const relFiles = (res.stdout || "").trim().split(/\r?\n/).filter((l) => l.length > 0);
|
|
606
|
+
const absFiles = relFiles.map((f) => import_path5.default.resolve(absRoot, f));
|
|
607
|
+
const withMtime = [];
|
|
608
|
+
for (const f of absFiles) {
|
|
609
|
+
const s = await import_promises2.default.stat(f).catch(() => null);
|
|
610
|
+
withMtime.push({ file: f, mtime: s?.mtimeMs ?? 0 });
|
|
611
|
+
}
|
|
612
|
+
withMtime.sort((a, b) => b.mtime - a.mtime);
|
|
613
|
+
const totalFound = withMtime.length;
|
|
614
|
+
return { files: withMtime.slice(0, 100).map((f) => f.file), searchDir: abs, totalFound };
|
|
615
|
+
}
|
|
575
616
|
};
|
|
576
617
|
}
|
|
577
618
|
});
|
|
@@ -613,131 +654,58 @@ var import_zod = require("zod");
|
|
|
613
654
|
init_config();
|
|
614
655
|
|
|
615
656
|
// tools/warp_grep/agent/parser.ts
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
657
|
+
function parseReadLines(linesStr) {
|
|
658
|
+
const ranges = [];
|
|
659
|
+
for (const rangeStr of linesStr.split(",")) {
|
|
660
|
+
const trimmed = rangeStr.trim();
|
|
661
|
+
if (!trimmed) continue;
|
|
662
|
+
const parts = trimmed.split("-").map((v) => parseInt(v.trim(), 10));
|
|
663
|
+
if (parts.length >= 2 && Number.isFinite(parts[0]) && Number.isFinite(parts[1])) {
|
|
664
|
+
ranges.push([parts[0], parts[1]]);
|
|
665
|
+
} else if (Number.isFinite(parts[0])) {
|
|
666
|
+
ranges.push([parts[0], parts[0]]);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
if (ranges.length === 1) return { start: ranges[0][0], end: ranges[0][1] };
|
|
670
|
+
if (ranges.length > 1) return { lines: ranges };
|
|
671
|
+
return {};
|
|
619
672
|
}
|
|
620
|
-
function
|
|
621
|
-
const
|
|
622
|
-
const
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
const
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
const paramRegex = /<parameter=([a-z_][a-z0-9_]*)>([\s\S]*?)<\/parameter>/gi;
|
|
630
|
-
let paramMatch;
|
|
631
|
-
while ((paramMatch = paramRegex.exec(body)) !== null) {
|
|
632
|
-
params[paramMatch[1].toLowerCase()] = paramMatch[2].trim();
|
|
673
|
+
function parseFinishFiles(filesStr) {
|
|
674
|
+
const files = [];
|
|
675
|
+
for (const line of filesStr.trim().split(/\s+/)) {
|
|
676
|
+
const trimmed = line.trim();
|
|
677
|
+
if (!trimmed) continue;
|
|
678
|
+
const colonIdx = trimmed.indexOf(":");
|
|
679
|
+
if (colonIdx === -1) {
|
|
680
|
+
files.push({ path: trimmed, lines: "*" });
|
|
681
|
+
continue;
|
|
633
682
|
}
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
if (!directPath && command) {
|
|
650
|
-
const tokens = command.trim().split(/\s+/);
|
|
651
|
-
const pathTokens = tokens.slice(1).filter((t) => !t.startsWith("-") && !t.startsWith("|") && !t.startsWith("\\("));
|
|
652
|
-
if (pathTokens.length > 0) {
|
|
653
|
-
dirPath = pathTokens[0];
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
tools.push({ name: "list_directory", arguments: { path: dirPath, pattern: params.pattern || null } });
|
|
657
|
-
} else if (funcName === "read") {
|
|
658
|
-
const filePath = params.path;
|
|
659
|
-
if (!filePath) continue;
|
|
660
|
-
const args = { path: filePath };
|
|
661
|
-
const linesStr = params.lines;
|
|
662
|
-
if (linesStr) {
|
|
663
|
-
const ranges = [];
|
|
664
|
-
for (const rangeStr of linesStr.split(",")) {
|
|
665
|
-
const trimmed = rangeStr.trim();
|
|
666
|
-
if (!trimmed) continue;
|
|
667
|
-
const [s, e] = trimmed.split("-").map((v) => parseInt(v.trim(), 10));
|
|
668
|
-
if (Number.isFinite(s) && Number.isFinite(e)) {
|
|
669
|
-
ranges.push([s, e]);
|
|
670
|
-
} else if (Number.isFinite(s)) {
|
|
671
|
-
ranges.push([s, s]);
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
if (ranges.length === 1) {
|
|
675
|
-
args.start = ranges[0][0];
|
|
676
|
-
args.end = ranges[0][1];
|
|
677
|
-
} else if (ranges.length > 1) {
|
|
678
|
-
args.lines = ranges;
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
tools.push({ name: "read", arguments: args });
|
|
682
|
-
} else if (funcName === "finish") {
|
|
683
|
-
if (params.result && !params.files) {
|
|
684
|
-
tools.push({ name: "finish", arguments: { files: [], textResult: params.result } });
|
|
685
|
-
continue;
|
|
686
|
-
}
|
|
687
|
-
const filesStr = params.files;
|
|
688
|
-
if (!filesStr) {
|
|
689
|
-
tools.push({ name: "finish", arguments: { files: [], textResult: "No relevant code found." } });
|
|
690
|
-
continue;
|
|
691
|
-
}
|
|
692
|
-
const files = [];
|
|
693
|
-
for (const line of filesStr.split("\n")) {
|
|
694
|
-
const trimmed = line.trim();
|
|
695
|
-
if (!trimmed) continue;
|
|
696
|
-
const colonIdx = trimmed.indexOf(":");
|
|
697
|
-
if (colonIdx === -1) {
|
|
698
|
-
files.push({ path: trimmed, lines: "*" });
|
|
699
|
-
} else {
|
|
700
|
-
const filePath = trimmed.slice(0, colonIdx);
|
|
701
|
-
const rangesPart = trimmed.slice(colonIdx + 1);
|
|
702
|
-
const ranges = [];
|
|
703
|
-
for (const rangeStr of rangesPart.split(",")) {
|
|
704
|
-
const rt = rangeStr.trim();
|
|
705
|
-
if (!rt || rt === "*") {
|
|
706
|
-
files.push({ path: filePath, lines: "*" });
|
|
707
|
-
break;
|
|
708
|
-
}
|
|
709
|
-
const [s, e] = rt.split("-").map((v) => parseInt(v.trim(), 10));
|
|
710
|
-
if (Number.isFinite(s) && Number.isFinite(e)) {
|
|
711
|
-
ranges.push([s, e]);
|
|
712
|
-
} else if (Number.isFinite(s)) {
|
|
713
|
-
ranges.push([s, s]);
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
if (ranges.length > 0) {
|
|
717
|
-
files.push({ path: filePath, lines: ranges });
|
|
718
|
-
} else if (!files.some((f) => f.path === filePath)) {
|
|
719
|
-
files.push({ path: filePath, lines: "*" });
|
|
720
|
-
}
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
if (files.length > 0) {
|
|
724
|
-
tools.push({ name: "finish", arguments: { files } });
|
|
725
|
-
} else {
|
|
726
|
-
tools.push({ name: "finish", arguments: { files: [], textResult: filesStr } });
|
|
683
|
+
const filePath = trimmed.slice(0, colonIdx);
|
|
684
|
+
const rangesPart = trimmed.slice(colonIdx + 1);
|
|
685
|
+
if (!rangesPart.trim() || rangesPart.trim() === "*") {
|
|
686
|
+
files.push({ path: filePath, lines: "*" });
|
|
687
|
+
continue;
|
|
688
|
+
}
|
|
689
|
+
const ranges = [];
|
|
690
|
+
for (const rangeStr of rangesPart.split(",")) {
|
|
691
|
+
const rt = rangeStr.trim();
|
|
692
|
+
if (!rt) continue;
|
|
693
|
+
const parts = rt.split("-").map((v) => parseInt(v.trim(), 10));
|
|
694
|
+
if (parts.length >= 2 && Number.isFinite(parts[0]) && Number.isFinite(parts[1])) {
|
|
695
|
+
ranges.push([parts[0], parts[1]]);
|
|
696
|
+
} else if (Number.isFinite(parts[0])) {
|
|
697
|
+
ranges.push([parts[0], parts[0]]);
|
|
727
698
|
}
|
|
728
699
|
}
|
|
700
|
+
files.push({ path: filePath, lines: ranges.length > 0 ? ranges : "*" });
|
|
729
701
|
}
|
|
730
|
-
return
|
|
702
|
+
return files;
|
|
703
|
+
}
|
|
704
|
+
function extractPathFromCommand(command) {
|
|
705
|
+
const tokens = command.trim().split(/\s+/);
|
|
706
|
+
const pathTokens = tokens.slice(1).filter((t) => !t.startsWith("-") && !t.startsWith("|") && !t.startsWith("\\("));
|
|
707
|
+
return pathTokens[0] || ".";
|
|
731
708
|
}
|
|
732
|
-
var LLMResponseParser = class {
|
|
733
|
-
parse(text) {
|
|
734
|
-
if (typeof text !== "string") {
|
|
735
|
-
throw new TypeError("Command text must be a string.");
|
|
736
|
-
}
|
|
737
|
-
const withoutThink = text.replace(/<think>[\s\S]*?<\/think>/gi, "");
|
|
738
|
-
return parseQwen3ToolCalls(withoutThink);
|
|
739
|
-
}
|
|
740
|
-
};
|
|
741
709
|
|
|
742
710
|
// tools/warp_grep/agent/tools/grep.ts
|
|
743
711
|
async function toolGrep(provider, args) {
|
|
@@ -788,29 +756,42 @@ async function toolRead(provider, args) {
|
|
|
788
756
|
|
|
789
757
|
// tools/warp_grep/agent/tools/list_directory.ts
|
|
790
758
|
init_config();
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
const
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
759
|
+
var import_path = __toESM(require("path"), 1);
|
|
760
|
+
async function toolListDirectory(provider, args, repoRoot) {
|
|
761
|
+
const maxResults = args.maxResults ?? AGENT_CONFIG.MAX_LIST_RESULTS;
|
|
762
|
+
const maxDepth = args.maxDepth ?? AGENT_CONFIG.MAX_LIST_DEPTH;
|
|
763
|
+
const entries = await provider.listDirectory({
|
|
764
|
+
path: args.path,
|
|
765
|
+
pattern: args.pattern ?? null,
|
|
766
|
+
maxResults,
|
|
767
|
+
maxDepth
|
|
768
|
+
});
|
|
769
|
+
if (!entries.length) return "empty";
|
|
770
|
+
if (repoRoot) {
|
|
771
|
+
const absRoot = import_path.default.resolve(repoRoot);
|
|
772
|
+
const lines = entries.map((e) => import_path.default.join(absRoot, e.path));
|
|
773
|
+
return lines.join("\n");
|
|
774
|
+
}
|
|
775
|
+
return entries.map((e) => e.path).join("\n");
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// tools/warp_grep/agent/tools/glob.ts
|
|
779
|
+
async function toolGlob(provider, args) {
|
|
780
|
+
const res = await provider.glob(args);
|
|
781
|
+
if (res.error) {
|
|
782
|
+
return res.error;
|
|
783
|
+
}
|
|
784
|
+
if (!res.files.length) {
|
|
785
|
+
return "no matches";
|
|
786
|
+
}
|
|
787
|
+
const header = `Found ${res.totalFound} file(s) matching "${args.pattern}" within ${res.searchDir}, sorted by modification time (newest first):`;
|
|
788
|
+
const body = res.files.join("\n");
|
|
789
|
+
const truncated = res.totalFound > res.files.length ? `
|
|
790
|
+
[${res.totalFound - res.files.length} files truncated]` : "";
|
|
791
|
+
return `${header}
|
|
792
|
+
---
|
|
793
|
+
${body}
|
|
794
|
+
---${truncated}`;
|
|
814
795
|
}
|
|
815
796
|
|
|
816
797
|
// tools/warp_grep/agent/tools/finish.ts
|
|
@@ -879,32 +860,21 @@ function mergeRanges(ranges) {
|
|
|
879
860
|
return merged;
|
|
880
861
|
}
|
|
881
862
|
|
|
882
|
-
// tools/warp_grep/agent/formatter.ts
|
|
883
|
-
var ToolOutputFormatter = class {
|
|
884
|
-
format(toolName, _args, output, options = {}) {
|
|
885
|
-
const name = (toolName ?? "").trim();
|
|
886
|
-
if (!name) {
|
|
887
|
-
return "";
|
|
888
|
-
}
|
|
889
|
-
const payload = output?.toString?.()?.trim?.() ?? "";
|
|
890
|
-
const isError = Boolean(options.isError);
|
|
891
|
-
if (!payload && !isError) {
|
|
892
|
-
return "";
|
|
893
|
-
}
|
|
894
|
-
return `<tool_response>
|
|
895
|
-
${payload}
|
|
896
|
-
</tool_response>`;
|
|
897
|
-
}
|
|
898
|
-
};
|
|
899
|
-
var sharedFormatter = new ToolOutputFormatter();
|
|
900
|
-
function formatAgentToolOutput(toolName, args, output, options = {}) {
|
|
901
|
-
return sharedFormatter.format(toolName, args, output, options);
|
|
902
|
-
}
|
|
903
|
-
|
|
904
863
|
// tools/warp_grep/agent/helpers.ts
|
|
905
|
-
var
|
|
864
|
+
var import_path2 = __toESM(require("path"), 1);
|
|
906
865
|
init_config();
|
|
907
866
|
var TRUNCATED_MARKER = "[truncated for context limit]";
|
|
867
|
+
function getMessageSize(m) {
|
|
868
|
+
if (m.role === "tool") return m.content.length;
|
|
869
|
+
if (m.role === "assistant") {
|
|
870
|
+
let size = typeof m.content === "string" ? m.content.length : 0;
|
|
871
|
+
if (m.tool_calls) {
|
|
872
|
+
size += m.tool_calls.reduce((s, tc) => s + tc.function.name.length + tc.function.arguments.length, 0);
|
|
873
|
+
}
|
|
874
|
+
return size;
|
|
875
|
+
}
|
|
876
|
+
return m.content.length;
|
|
877
|
+
}
|
|
908
878
|
function formatTurnMessage(turnsUsed, maxTurns) {
|
|
909
879
|
const turnsRemaining = maxTurns - turnsUsed;
|
|
910
880
|
if (turnsRemaining === 1) {
|
|
@@ -915,7 +885,7 @@ You have used ${turnsUsed} turns, you only have 1 turn remaining. You have run o
|
|
|
915
885
|
You have used ${turnsUsed} turn${turnsUsed === 1 ? "" : "s"} and have ${turnsRemaining} remaining`;
|
|
916
886
|
}
|
|
917
887
|
function calculateContextBudget(messages) {
|
|
918
|
-
const totalChars = messages.reduce((sum, m) => sum + m
|
|
888
|
+
const totalChars = messages.reduce((sum, m) => sum + getMessageSize(m), 0);
|
|
919
889
|
const maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS;
|
|
920
890
|
const percent = Math.round(totalChars / maxChars * 100);
|
|
921
891
|
const usedK = Math.round(totalChars / 1e3);
|
|
@@ -924,24 +894,21 @@ function calculateContextBudget(messages) {
|
|
|
924
894
|
}
|
|
925
895
|
async function buildInitialState(repoRoot, searchTerm, provider, options) {
|
|
926
896
|
const budget = calculateContextBudget([]);
|
|
927
|
-
const turnTag = `
|
|
897
|
+
const turnTag = `You have used 0 turns and have ${AGENT_CONFIG.MAX_TURNS} remaining`;
|
|
928
898
|
const treeDepth = options?.search_type === "node_modules" ? 1 : 2;
|
|
899
|
+
const absRoot = import_path2.default.resolve(repoRoot);
|
|
929
900
|
try {
|
|
930
901
|
const entries = await provider.listDirectory({
|
|
931
902
|
path: ".",
|
|
932
903
|
maxResults: AGENT_CONFIG.MAX_OUTPUT_LINES,
|
|
933
904
|
maxDepth: treeDepth
|
|
934
905
|
});
|
|
935
|
-
const
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
});
|
|
940
|
-
const repoName = import_path.default.basename(repoRoot);
|
|
941
|
-
const treeOutput = treeLines.length > 0 ? `${repoName}/
|
|
942
|
-
${treeLines.join("\n")}` : `${repoName}/`;
|
|
906
|
+
const lines = [absRoot];
|
|
907
|
+
for (const e of entries) {
|
|
908
|
+
lines.push(import_path2.default.join(absRoot, e.path));
|
|
909
|
+
}
|
|
943
910
|
return `<repo_structure>
|
|
944
|
-
${
|
|
911
|
+
${lines.join("\n")}
|
|
945
912
|
</repo_structure>
|
|
946
913
|
|
|
947
914
|
<search_string>
|
|
@@ -950,9 +917,8 @@ ${searchTerm}
|
|
|
950
917
|
${budget}
|
|
951
918
|
${turnTag}`;
|
|
952
919
|
} catch {
|
|
953
|
-
const repoName = import_path.default.basename(repoRoot);
|
|
954
920
|
return `<repo_structure>
|
|
955
|
-
${
|
|
921
|
+
${absRoot}
|
|
956
922
|
</repo_structure>
|
|
957
923
|
|
|
958
924
|
<search_string>
|
|
@@ -963,26 +929,32 @@ ${turnTag}`;
|
|
|
963
929
|
}
|
|
964
930
|
}
|
|
965
931
|
function enforceContextLimit(messages, maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS) {
|
|
966
|
-
const getTotalChars = () => messages.reduce((sum, m) => sum + m
|
|
932
|
+
const getTotalChars = () => messages.reduce((sum, m) => sum + getMessageSize(m), 0);
|
|
967
933
|
if (getTotalChars() <= maxChars) {
|
|
968
934
|
return messages;
|
|
969
935
|
}
|
|
970
|
-
const
|
|
936
|
+
const truncatableIndices = [];
|
|
971
937
|
let firstUserSkipped = false;
|
|
972
938
|
for (let i = 0; i < messages.length; i++) {
|
|
973
|
-
|
|
939
|
+
const m = messages[i];
|
|
940
|
+
if (m.role === "tool") {
|
|
941
|
+
truncatableIndices.push(i);
|
|
942
|
+
} else if (m.role === "user") {
|
|
974
943
|
if (!firstUserSkipped) {
|
|
975
944
|
firstUserSkipped = true;
|
|
976
945
|
continue;
|
|
977
946
|
}
|
|
978
|
-
|
|
947
|
+
truncatableIndices.push(i);
|
|
979
948
|
}
|
|
980
949
|
}
|
|
981
|
-
for (const idx of
|
|
950
|
+
for (const idx of truncatableIndices) {
|
|
982
951
|
if (getTotalChars() <= maxChars) {
|
|
983
952
|
break;
|
|
984
953
|
}
|
|
985
|
-
|
|
954
|
+
const m = messages[idx];
|
|
955
|
+
if (m.role === "tool" && m.content !== TRUNCATED_MARKER) {
|
|
956
|
+
messages[idx] = { role: "tool", tool_call_id: m.tool_call_id, content: TRUNCATED_MARKER };
|
|
957
|
+
} else if (m.role === "user" && m.content !== TRUNCATED_MARKER) {
|
|
986
958
|
messages[idx] = { role: "user", content: TRUNCATED_MARKER };
|
|
987
959
|
}
|
|
988
960
|
}
|
|
@@ -995,7 +967,7 @@ var import_openai = __toESM(require("openai"), 1);
|
|
|
995
967
|
// package.json
|
|
996
968
|
var package_default = {
|
|
997
969
|
name: "@morphllm/morphsdk",
|
|
998
|
-
version: "0.2.
|
|
970
|
+
version: "0.2.145",
|
|
999
971
|
description: "TypeScript SDK and CLI for Morph Fast Apply integration",
|
|
1000
972
|
type: "module",
|
|
1001
973
|
main: "./dist/index.cjs",
|
|
@@ -1234,9 +1206,115 @@ var package_default = {
|
|
|
1234
1206
|
var SDK_VERSION = package_default.version;
|
|
1235
1207
|
|
|
1236
1208
|
// tools/warp_grep/agent/runner.ts
|
|
1237
|
-
var
|
|
1238
|
-
var parser = new LLMResponseParser();
|
|
1209
|
+
var import_path3 = __toESM(require("path"), 1);
|
|
1239
1210
|
var DEFAULT_API_URL = "https://api.morphllm.com";
|
|
1211
|
+
var TOOL_SPECS = [
|
|
1212
|
+
{
|
|
1213
|
+
type: "function",
|
|
1214
|
+
function: {
|
|
1215
|
+
name: "list_directory",
|
|
1216
|
+
description: "Execute ls or find commands to explore directory structure. Max 500 results. Common junk directories are excluded automatically.",
|
|
1217
|
+
parameters: {
|
|
1218
|
+
type: "object",
|
|
1219
|
+
properties: {
|
|
1220
|
+
command: {
|
|
1221
|
+
type: "string",
|
|
1222
|
+
description: "Full ls or find command (e.g. ls -la src/, find . -maxdepth 2 -type f -name '*.py', find . -type d, ls -d */)."
|
|
1223
|
+
}
|
|
1224
|
+
},
|
|
1225
|
+
required: ["command"]
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
},
|
|
1229
|
+
{
|
|
1230
|
+
type: "function",
|
|
1231
|
+
function: {
|
|
1232
|
+
name: "grep_search",
|
|
1233
|
+
description: "Search for a regex pattern in file contents. Returns matching lines with file paths and line numbers. Case-insensitive. Respects .gitignore.",
|
|
1234
|
+
parameters: {
|
|
1235
|
+
type: "object",
|
|
1236
|
+
properties: {
|
|
1237
|
+
pattern: {
|
|
1238
|
+
type: "string",
|
|
1239
|
+
description: "Regex pattern to search for in file contents (e.g. 'class\\s+\\w+Error', 'import|require|from', 'def (get|set|update)_user')."
|
|
1240
|
+
},
|
|
1241
|
+
path: {
|
|
1242
|
+
type: "string",
|
|
1243
|
+
description: "File or directory to search in. Defaults to current working directory."
|
|
1244
|
+
},
|
|
1245
|
+
glob: {
|
|
1246
|
+
type: "string",
|
|
1247
|
+
description: "Glob pattern to filter files (e.g. '*.py', '*.{ts,tsx,js,jsx,py,go}', 'src/**/*.go', '!*.test.*')."
|
|
1248
|
+
},
|
|
1249
|
+
limit: {
|
|
1250
|
+
type: "integer",
|
|
1251
|
+
description: "Limit output to first N matching lines. Shows all matches if not specified."
|
|
1252
|
+
}
|
|
1253
|
+
},
|
|
1254
|
+
required: ["pattern"]
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
},
|
|
1258
|
+
{
|
|
1259
|
+
type: "function",
|
|
1260
|
+
function: {
|
|
1261
|
+
name: "glob",
|
|
1262
|
+
description: "Find files by name/extension using glob patterns. Returns absolute paths sorted by modification time (newest first). Respects .gitignore. Max 100 results.",
|
|
1263
|
+
parameters: {
|
|
1264
|
+
type: "object",
|
|
1265
|
+
properties: {
|
|
1266
|
+
pattern: {
|
|
1267
|
+
type: "string",
|
|
1268
|
+
description: "Glob pattern to match files (e.g. '*.py', 'src/**/*.js', '*.{ts,tsx}', 'test_*.py')."
|
|
1269
|
+
},
|
|
1270
|
+
path: {
|
|
1271
|
+
type: "string",
|
|
1272
|
+
description: "Directory to search in. Defaults to repository root."
|
|
1273
|
+
}
|
|
1274
|
+
},
|
|
1275
|
+
required: ["pattern"]
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
},
|
|
1279
|
+
{
|
|
1280
|
+
type: "function",
|
|
1281
|
+
function: {
|
|
1282
|
+
name: "read",
|
|
1283
|
+
description: "Read entire files or specific line ranges using absolute paths.",
|
|
1284
|
+
parameters: {
|
|
1285
|
+
type: "object",
|
|
1286
|
+
properties: {
|
|
1287
|
+
path: {
|
|
1288
|
+
type: "string",
|
|
1289
|
+
description: "File path to read, using absolute path (e.g. '/home/ubuntu/repo/src/main.py' or windows path)."
|
|
1290
|
+
},
|
|
1291
|
+
lines: {
|
|
1292
|
+
type: "string",
|
|
1293
|
+
description: "Optional line range (e.g. '1-50' or '1-20,45-80'). Omit to read entire file."
|
|
1294
|
+
}
|
|
1295
|
+
},
|
|
1296
|
+
required: ["path"]
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
},
|
|
1300
|
+
{
|
|
1301
|
+
type: "function",
|
|
1302
|
+
function: {
|
|
1303
|
+
name: "finish",
|
|
1304
|
+
description: "Submit final answer with all relevant code locations. Include imports and over-include rather than miss context.",
|
|
1305
|
+
parameters: {
|
|
1306
|
+
type: "object",
|
|
1307
|
+
properties: {
|
|
1308
|
+
files: {
|
|
1309
|
+
type: "string",
|
|
1310
|
+
description: "One file per line as path:lines (e.g. 'src/auth.py:1-15,25-50\\nsrc/user.py'). Omit line range to include entire file."
|
|
1311
|
+
}
|
|
1312
|
+
},
|
|
1313
|
+
required: ["files"]
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
];
|
|
1240
1318
|
async function callModel(messages, model, options = {}) {
|
|
1241
1319
|
const baseUrl = options.morphApiUrl || DEFAULT_API_URL;
|
|
1242
1320
|
const apiKey = options.morphApiKey || process.env.MORPH_API_KEY || "";
|
|
@@ -1257,8 +1335,9 @@ async function callModel(messages, model, options = {}) {
|
|
|
1257
1335
|
data = await client.chat.completions.create({
|
|
1258
1336
|
model,
|
|
1259
1337
|
temperature: 0,
|
|
1260
|
-
max_tokens:
|
|
1338
|
+
max_tokens: 2048,
|
|
1261
1339
|
messages,
|
|
1340
|
+
tools: TOOL_SPECS,
|
|
1262
1341
|
...options.search_type ? { search_type: options.search_type } : {}
|
|
1263
1342
|
});
|
|
1264
1343
|
} catch (error) {
|
|
@@ -1270,187 +1349,87 @@ async function callModel(messages, model, options = {}) {
|
|
|
1270
1349
|
throw error;
|
|
1271
1350
|
}
|
|
1272
1351
|
const choice = data?.choices?.[0];
|
|
1273
|
-
const
|
|
1274
|
-
if (
|
|
1275
|
-
|
|
1352
|
+
const message = choice?.message;
|
|
1353
|
+
if (!message) {
|
|
1354
|
+
if (attempt === MAX_EMPTY_RETRIES) {
|
|
1355
|
+
throw new Error("Invalid response from model: no message in response");
|
|
1356
|
+
}
|
|
1357
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
1358
|
+
continue;
|
|
1359
|
+
}
|
|
1360
|
+
const toolCalls = (message.tool_calls || []).map((tc) => ({
|
|
1361
|
+
id: tc.id,
|
|
1362
|
+
type: "function",
|
|
1363
|
+
function: { name: tc.function.name, arguments: tc.function.arguments }
|
|
1364
|
+
}));
|
|
1365
|
+
if (message.content || toolCalls.length > 0) {
|
|
1366
|
+
return { content: message.content ?? null, tool_calls: toolCalls };
|
|
1276
1367
|
}
|
|
1277
1368
|
if (attempt === MAX_EMPTY_RETRIES) {
|
|
1278
1369
|
const finishReason = choice?.finish_reason ?? "unknown";
|
|
1279
|
-
const hasToolCalls = Array.isArray(choice?.message?.tool_calls) && choice.message.tool_calls.length > 0;
|
|
1280
|
-
const choicesLen = data?.choices?.length ?? 0;
|
|
1281
|
-
const contentType = content === null ? "null" : content === void 0 ? "undefined" : typeof content;
|
|
1282
1370
|
throw new Error(
|
|
1283
|
-
`Invalid response from model: content
|
|
1371
|
+
`Invalid response from model: no content and no tool_calls, finish_reason=${finishReason}`
|
|
1284
1372
|
);
|
|
1285
1373
|
}
|
|
1286
1374
|
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
1287
1375
|
}
|
|
1288
1376
|
throw new Error("Invalid response from model");
|
|
1289
1377
|
}
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
}).catch((e) => {
|
|
1317
|
-
const errMsg = e instanceof Error ? e.message : String(e);
|
|
1318
|
-
console.error(`[warp_grep] Morph API call failed on turn ${turn}:`, errMsg);
|
|
1319
|
-
errors.push({ message: errMsg });
|
|
1320
|
-
return "";
|
|
1321
|
-
});
|
|
1322
|
-
turnMetrics.morph_api_ms = Date.now() - modelCallStart;
|
|
1323
|
-
if (!assistantContent) {
|
|
1324
|
-
console.error(`[warp_grep] Empty response from Morph API on turn ${turn}. Errors so far:`, errors);
|
|
1325
|
-
timings.turns.push(turnMetrics);
|
|
1326
|
-
break;
|
|
1327
|
-
}
|
|
1328
|
-
messages.push({ role: "assistant", content: assistantContent });
|
|
1329
|
-
const toolCalls = parser.parse(assistantContent);
|
|
1330
|
-
if (toolCalls.length === 0) {
|
|
1331
|
-
console.error(`[warp_grep] No tool calls parsed on turn ${turn}. Assistant content (first 500 chars):`, assistantContent.slice(0, 500));
|
|
1332
|
-
errors.push({ message: "No tool calls produced by the model. Your MCP is likely out of date! Update it by running: rm -rf ~/.npm/_npx && npm cache clean --force && npx -y @morphllm/morphmcp@latest" });
|
|
1333
|
-
terminationReason = "terminated";
|
|
1334
|
-
timings.turns.push(turnMetrics);
|
|
1335
|
-
break;
|
|
1336
|
-
}
|
|
1337
|
-
const finishCalls = toolCalls.filter((c) => c.name === "finish");
|
|
1338
|
-
const grepCalls = toolCalls.filter((c) => c.name === "grep");
|
|
1339
|
-
const listDirCalls = toolCalls.filter((c) => c.name === "list_directory");
|
|
1340
|
-
const readCalls = toolCalls.filter((c) => c.name === "read");
|
|
1341
|
-
const skipCalls = toolCalls.filter((c) => c.name === "_skip");
|
|
1342
|
-
const formatted = [];
|
|
1343
|
-
for (const c of skipCalls) {
|
|
1344
|
-
const msg = c.arguments?.message || "Command skipped due to parsing error";
|
|
1345
|
-
formatted.push(msg);
|
|
1346
|
-
}
|
|
1347
|
-
const allPromises = [];
|
|
1348
|
-
for (const c of grepCalls) {
|
|
1349
|
-
const args = c.arguments ?? {};
|
|
1350
|
-
allPromises.push(
|
|
1351
|
-
toolGrep(provider, args).then(
|
|
1352
|
-
({ output }) => formatAgentToolOutput("grep", args, output),
|
|
1353
|
-
(err) => formatAgentToolOutput("grep", args, String(err), { isError: true })
|
|
1354
|
-
)
|
|
1355
|
-
);
|
|
1356
|
-
}
|
|
1357
|
-
for (const c of listDirCalls) {
|
|
1358
|
-
const args = c.arguments ?? {};
|
|
1359
|
-
allPromises.push(
|
|
1360
|
-
toolListDirectory(provider, args).then(
|
|
1361
|
-
(p) => formatAgentToolOutput("list_directory", args, p),
|
|
1362
|
-
(err) => formatAgentToolOutput("list_directory", args, String(err), { isError: true })
|
|
1363
|
-
)
|
|
1364
|
-
);
|
|
1365
|
-
}
|
|
1366
|
-
for (const c of readCalls) {
|
|
1367
|
-
const args = c.arguments ?? {};
|
|
1368
|
-
allPromises.push(
|
|
1369
|
-
toolRead(provider, args).then(
|
|
1370
|
-
(p) => formatAgentToolOutput("read", args, p),
|
|
1371
|
-
(err) => formatAgentToolOutput("read", args, String(err), { isError: true })
|
|
1372
|
-
)
|
|
1373
|
-
);
|
|
1374
|
-
}
|
|
1375
|
-
const toolExecStart = Date.now();
|
|
1376
|
-
const allResults = await Promise.all(allPromises);
|
|
1377
|
-
turnMetrics.local_tools_ms = Date.now() - toolExecStart;
|
|
1378
|
-
for (const result of allResults) {
|
|
1379
|
-
formatted.push(result);
|
|
1378
|
+
function safeParseJSON(s) {
|
|
1379
|
+
try {
|
|
1380
|
+
return JSON.parse(s);
|
|
1381
|
+
} catch {
|
|
1382
|
+
return {};
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
async function executeTool(provider, name, args, repoRoot) {
|
|
1386
|
+
switch (name) {
|
|
1387
|
+
case "grep_search": {
|
|
1388
|
+
const grepArgs = {
|
|
1389
|
+
pattern: args.pattern,
|
|
1390
|
+
path: args.path || "."
|
|
1391
|
+
};
|
|
1392
|
+
if (args.glob) grepArgs.glob = args.glob;
|
|
1393
|
+
if (args.case_sensitive !== void 0) grepArgs.case_sensitive = args.case_sensitive;
|
|
1394
|
+
const result = await toolGrep(provider, grepArgs);
|
|
1395
|
+
let output = result.output;
|
|
1396
|
+
if (args.limit && typeof args.limit === "number") {
|
|
1397
|
+
const lines = output.split("\n");
|
|
1398
|
+
if (lines.length > args.limit) {
|
|
1399
|
+
output = lines.slice(0, args.limit).join("\n") + `
|
|
1400
|
+
... (truncated at ${args.limit} lines)`;
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
return output;
|
|
1380
1404
|
}
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1405
|
+
case "glob": {
|
|
1406
|
+
return toolGlob(provider, {
|
|
1407
|
+
pattern: args.pattern,
|
|
1408
|
+
path: args.path
|
|
1409
|
+
});
|
|
1385
1410
|
}
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
const files = fc.arguments?.files ?? [];
|
|
1390
|
-
const textResult = fc.arguments?.textResult;
|
|
1391
|
-
finishMeta = { files };
|
|
1392
|
-
terminationReason = "completed";
|
|
1393
|
-
if (files.length === 0) {
|
|
1394
|
-
const payload2 = textResult || "No relevant code found.";
|
|
1395
|
-
timings.turns.push(turnMetrics);
|
|
1396
|
-
timings.total_ms = Date.now() - totalStart;
|
|
1397
|
-
return {
|
|
1398
|
-
terminationReason: "completed",
|
|
1399
|
-
messages,
|
|
1400
|
-
finish: { payload: payload2, metadata: finishMeta },
|
|
1401
|
-
timings
|
|
1402
|
-
};
|
|
1403
|
-
}
|
|
1404
|
-
break;
|
|
1411
|
+
case "list_directory": {
|
|
1412
|
+
const dirPath = extractPathFromCommand(args.command || ".");
|
|
1413
|
+
return toolListDirectory(provider, { path: dirPath }, repoRoot);
|
|
1405
1414
|
}
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
for (const f of finishMeta.files) {
|
|
1413
|
-
const ranges = f.lines === "*" ? "*" : Array.isArray(f.lines) ? f.lines.map(([s, e]) => `${s}-${e}`).join(", ") : "*";
|
|
1414
|
-
parts.push(`- ${f.path}: ${ranges}`);
|
|
1415
|
-
}
|
|
1416
|
-
const payload = parts.join("\n");
|
|
1417
|
-
const finishResolutionStart = Date.now();
|
|
1418
|
-
const fileReadErrors = [];
|
|
1419
|
-
const resolved = await readFinishFiles(
|
|
1420
|
-
repoRoot,
|
|
1421
|
-
finishMeta.files,
|
|
1422
|
-
async (p, s, e) => {
|
|
1423
|
-
try {
|
|
1424
|
-
const rr = await provider.read({ path: p, start: s, end: e });
|
|
1425
|
-
return rr.lines.map((l) => {
|
|
1426
|
-
const idx = l.indexOf("|");
|
|
1427
|
-
return idx >= 0 ? l.slice(idx + 1) : l;
|
|
1428
|
-
});
|
|
1429
|
-
} catch (err) {
|
|
1430
|
-
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
1431
|
-
fileReadErrors.push({ path: p, error: errorMsg });
|
|
1432
|
-
console.error(`[warp_grep] Failed to read file: ${p} - ${errorMsg}`);
|
|
1433
|
-
return [`[couldn't find: ${p}]`];
|
|
1415
|
+
case "read": {
|
|
1416
|
+
const readArgs = {
|
|
1417
|
+
path: args.path
|
|
1418
|
+
};
|
|
1419
|
+
if (args.lines && typeof args.lines === "string") {
|
|
1420
|
+
Object.assign(readArgs, parseReadLines(args.lines));
|
|
1434
1421
|
}
|
|
1422
|
+
return toolRead(provider, readArgs);
|
|
1435
1423
|
}
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
if (fileReadErrors.length > 0) {
|
|
1439
|
-
errors.push(...fileReadErrors.map((e) => ({ message: `File read error: ${e.path} - ${e.error}` })));
|
|
1424
|
+
default:
|
|
1425
|
+
return `Unknown tool: ${name}`;
|
|
1440
1426
|
}
|
|
1441
|
-
timings.total_ms = Date.now() - totalStart;
|
|
1442
|
-
return {
|
|
1443
|
-
terminationReason: "completed",
|
|
1444
|
-
messages,
|
|
1445
|
-
finish: { payload, metadata: finishMeta, resolved },
|
|
1446
|
-
timings
|
|
1447
|
-
};
|
|
1448
1427
|
}
|
|
1449
1428
|
async function* runWarpGrepStreaming(config) {
|
|
1450
1429
|
const totalStart = Date.now();
|
|
1451
1430
|
const timeoutMs = config.timeout ?? AGENT_CONFIG.TIMEOUT_MS;
|
|
1452
1431
|
const timings = { turns: [], timeout_ms: timeoutMs };
|
|
1453
|
-
const repoRoot =
|
|
1432
|
+
const repoRoot = import_path3.default.resolve(config.repoRoot || process.cwd());
|
|
1454
1433
|
const model = config.model || DEFAULT_MODEL;
|
|
1455
1434
|
const messages = [];
|
|
1456
1435
|
const maxTurns = AGENT_CONFIG.MAX_TURNS;
|
|
@@ -1466,7 +1445,7 @@ async function* runWarpGrepStreaming(config) {
|
|
|
1466
1445
|
const turnMetrics = { turn, morph_api_ms: 0, local_tools_ms: 0 };
|
|
1467
1446
|
enforceContextLimit(messages);
|
|
1468
1447
|
const modelCallStart = Date.now();
|
|
1469
|
-
const
|
|
1448
|
+
const response = await callModel(messages, model, {
|
|
1470
1449
|
morphApiKey: config.morphApiKey,
|
|
1471
1450
|
morphApiUrl: config.morphApiUrl,
|
|
1472
1451
|
retryConfig: config.retryConfig,
|
|
@@ -1474,90 +1453,45 @@ async function* runWarpGrepStreaming(config) {
|
|
|
1474
1453
|
search_type: config.search_type
|
|
1475
1454
|
}).catch((e) => {
|
|
1476
1455
|
const errMsg = e instanceof Error ? e.message : String(e);
|
|
1477
|
-
console.error(`[warp_grep
|
|
1456
|
+
console.error(`[warp_grep] Morph API call failed on turn ${turn}:`, errMsg);
|
|
1478
1457
|
errors.push({ message: errMsg });
|
|
1479
|
-
return
|
|
1458
|
+
return null;
|
|
1480
1459
|
});
|
|
1481
1460
|
turnMetrics.morph_api_ms = Date.now() - modelCallStart;
|
|
1482
|
-
if (!
|
|
1483
|
-
console.error(`[warp_grep:stream] Empty response from Morph API on turn ${turn}. Errors so far:`, errors);
|
|
1461
|
+
if (!response) {
|
|
1484
1462
|
timings.turns.push(turnMetrics);
|
|
1485
1463
|
break;
|
|
1486
1464
|
}
|
|
1487
|
-
|
|
1488
|
-
|
|
1465
|
+
const toolCalls = response.tool_calls;
|
|
1466
|
+
messages.push({
|
|
1467
|
+
role: "assistant",
|
|
1468
|
+
content: response.content,
|
|
1469
|
+
...toolCalls.length > 0 ? { tool_calls: toolCalls } : {}
|
|
1470
|
+
});
|
|
1489
1471
|
if (toolCalls.length === 0) {
|
|
1490
|
-
console.error(`[warp_grep
|
|
1491
|
-
errors.push({ message: "No tool calls produced by the model.
|
|
1472
|
+
console.error(`[warp_grep] No tool calls on turn ${turn}. Content: ${(response.content || "").slice(0, 500)}`);
|
|
1473
|
+
errors.push({ message: "No tool calls produced by the model." });
|
|
1492
1474
|
terminationReason = "terminated";
|
|
1493
1475
|
timings.turns.push(turnMetrics);
|
|
1494
1476
|
break;
|
|
1495
1477
|
}
|
|
1496
1478
|
yield {
|
|
1497
1479
|
turn,
|
|
1498
|
-
toolCalls: toolCalls.map((
|
|
1499
|
-
name:
|
|
1500
|
-
arguments:
|
|
1480
|
+
toolCalls: toolCalls.map((tc) => ({
|
|
1481
|
+
name: tc.function.name,
|
|
1482
|
+
arguments: safeParseJSON(tc.function.arguments)
|
|
1501
1483
|
}))
|
|
1502
1484
|
};
|
|
1503
|
-
const
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
const formatted = [];
|
|
1509
|
-
for (const c of skipCalls) {
|
|
1510
|
-
const msg = c.arguments?.message || "Command skipped due to parsing error";
|
|
1511
|
-
formatted.push(msg);
|
|
1512
|
-
}
|
|
1513
|
-
const allPromises = [];
|
|
1514
|
-
for (const c of grepCalls) {
|
|
1515
|
-
const args = c.arguments ?? {};
|
|
1516
|
-
allPromises.push(
|
|
1517
|
-
toolGrep(provider, args).then(
|
|
1518
|
-
({ output }) => formatAgentToolOutput("grep", args, output),
|
|
1519
|
-
(err) => formatAgentToolOutput("grep", args, String(err), { isError: true })
|
|
1520
|
-
)
|
|
1521
|
-
);
|
|
1522
|
-
}
|
|
1523
|
-
for (const c of listDirCalls) {
|
|
1524
|
-
const args = c.arguments ?? {};
|
|
1525
|
-
allPromises.push(
|
|
1526
|
-
toolListDirectory(provider, args).then(
|
|
1527
|
-
(p) => formatAgentToolOutput("list_directory", args, p),
|
|
1528
|
-
(err) => formatAgentToolOutput("list_directory", args, String(err), { isError: true })
|
|
1529
|
-
)
|
|
1530
|
-
);
|
|
1531
|
-
}
|
|
1532
|
-
for (const c of readCalls) {
|
|
1533
|
-
const args = c.arguments ?? {};
|
|
1534
|
-
allPromises.push(
|
|
1535
|
-
toolRead(provider, args).then(
|
|
1536
|
-
(p) => formatAgentToolOutput("read", args, p),
|
|
1537
|
-
(err) => formatAgentToolOutput("read", args, String(err), { isError: true })
|
|
1538
|
-
)
|
|
1539
|
-
);
|
|
1540
|
-
}
|
|
1541
|
-
const toolExecStart = Date.now();
|
|
1542
|
-
const allResults = await Promise.all(allPromises);
|
|
1543
|
-
turnMetrics.local_tools_ms = Date.now() - toolExecStart;
|
|
1544
|
-
for (const result of allResults) {
|
|
1545
|
-
formatted.push(result);
|
|
1546
|
-
}
|
|
1547
|
-
if (formatted.length > 0) {
|
|
1548
|
-
const turnMessage = formatTurnMessage(turn, maxTurns);
|
|
1549
|
-
const contextBudget = calculateContextBudget(messages);
|
|
1550
|
-
messages.push({ role: "user", content: formatted.join("\n") + turnMessage + "\n" + contextBudget });
|
|
1551
|
-
}
|
|
1552
|
-
timings.turns.push(turnMetrics);
|
|
1553
|
-
if (finishCalls.length) {
|
|
1554
|
-
const fc = finishCalls[0];
|
|
1555
|
-
const files = fc.arguments?.files ?? [];
|
|
1556
|
-
const textResult = fc.arguments?.textResult;
|
|
1485
|
+
const finishCall = toolCalls.find((tc) => tc.function.name === "finish");
|
|
1486
|
+
if (finishCall) {
|
|
1487
|
+
const args = safeParseJSON(finishCall.function.arguments);
|
|
1488
|
+
const filesStr = args.files || "";
|
|
1489
|
+
const files = parseFinishFiles(filesStr);
|
|
1557
1490
|
finishMeta = { files };
|
|
1558
1491
|
terminationReason = "completed";
|
|
1559
1492
|
if (files.length === 0) {
|
|
1560
|
-
const payload2 =
|
|
1493
|
+
const payload2 = filesStr || "No relevant code found.";
|
|
1494
|
+
timings.turns.push(turnMetrics);
|
|
1561
1495
|
timings.total_ms = Date.now() - totalStart;
|
|
1562
1496
|
return {
|
|
1563
1497
|
terminationReason: "completed",
|
|
@@ -1566,8 +1500,25 @@ async function* runWarpGrepStreaming(config) {
|
|
|
1566
1500
|
timings
|
|
1567
1501
|
};
|
|
1568
1502
|
}
|
|
1503
|
+
timings.turns.push(turnMetrics);
|
|
1569
1504
|
break;
|
|
1570
1505
|
}
|
|
1506
|
+
const toolExecStart = Date.now();
|
|
1507
|
+
const results = await Promise.all(
|
|
1508
|
+
toolCalls.map(async (tc) => {
|
|
1509
|
+
const args = safeParseJSON(tc.function.arguments);
|
|
1510
|
+
const output = await executeTool(provider, tc.function.name, args, repoRoot).catch((err) => String(err));
|
|
1511
|
+
return { tool_call_id: tc.id, content: output };
|
|
1512
|
+
})
|
|
1513
|
+
);
|
|
1514
|
+
turnMetrics.local_tools_ms = Date.now() - toolExecStart;
|
|
1515
|
+
for (const result of results) {
|
|
1516
|
+
messages.push({ role: "tool", tool_call_id: result.tool_call_id, content: result.content });
|
|
1517
|
+
}
|
|
1518
|
+
const turnMsg = formatTurnMessage(turn, maxTurns);
|
|
1519
|
+
const budget = calculateContextBudget(messages);
|
|
1520
|
+
messages.push({ role: "user", content: turnMsg + "\n" + budget });
|
|
1521
|
+
timings.turns.push(turnMetrics);
|
|
1571
1522
|
}
|
|
1572
1523
|
if (terminationReason !== "completed" || !finishMeta) {
|
|
1573
1524
|
timings.total_ms = Date.now() - totalStart;
|
|
@@ -1611,6 +1562,14 @@ async function* runWarpGrepStreaming(config) {
|
|
|
1611
1562
|
timings
|
|
1612
1563
|
};
|
|
1613
1564
|
}
|
|
1565
|
+
async function runWarpGrep(config) {
|
|
1566
|
+
const gen = runWarpGrepStreaming(config);
|
|
1567
|
+
let result = await gen.next();
|
|
1568
|
+
while (!result.done) {
|
|
1569
|
+
result = await gen.next();
|
|
1570
|
+
}
|
|
1571
|
+
return result.value;
|
|
1572
|
+
}
|
|
1614
1573
|
|
|
1615
1574
|
// tools/warp_grep/providers/remote.ts
|
|
1616
1575
|
init_config();
|
|
@@ -1790,6 +1749,35 @@ var RemoteCommandsProvider = class {
|
|
|
1790
1749
|
return [];
|
|
1791
1750
|
}
|
|
1792
1751
|
}
|
|
1752
|
+
/**
|
|
1753
|
+
* Glob search - finds files matching a pattern.
|
|
1754
|
+
* Falls back to a grep --files approach via the listDir command.
|
|
1755
|
+
*/
|
|
1756
|
+
async glob(params) {
|
|
1757
|
+
const searchPath = params.path || this.repoRoot;
|
|
1758
|
+
try {
|
|
1759
|
+
const stdout = await this.commands.listDir(searchPath, 10);
|
|
1760
|
+
const allPaths = (stdout || "").trim().split(/\r?\n/).filter((p) => p.length > 0);
|
|
1761
|
+
const globToRegex = (glob) => {
|
|
1762
|
+
const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
1763
|
+
return new RegExp(escaped);
|
|
1764
|
+
};
|
|
1765
|
+
const regex = globToRegex(params.pattern);
|
|
1766
|
+
const matched = allPaths.filter((p) => {
|
|
1767
|
+
const name = p.split("/").pop() || "";
|
|
1768
|
+
return regex.test(name) && !shouldSkip(name);
|
|
1769
|
+
});
|
|
1770
|
+
const totalFound = matched.length;
|
|
1771
|
+
return { files: matched.slice(0, 100), searchDir: searchPath, totalFound };
|
|
1772
|
+
} catch (error) {
|
|
1773
|
+
return {
|
|
1774
|
+
files: [],
|
|
1775
|
+
searchDir: searchPath,
|
|
1776
|
+
totalFound: 0,
|
|
1777
|
+
error: `[GLOB ERROR] ${error instanceof Error ? error.message : String(error)}`
|
|
1778
|
+
};
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1793
1781
|
};
|
|
1794
1782
|
|
|
1795
1783
|
// tools/warp_grep/providers/code_storage_http.ts
|
|
@@ -1816,9 +1804,9 @@ function createCodeStorageHttpCommands(config) {
|
|
|
1816
1804
|
const { baseUrl, repoId, branch } = config;
|
|
1817
1805
|
const encodedRepoId = encodeURIComponent(repoId);
|
|
1818
1806
|
return {
|
|
1819
|
-
grep: (pattern,
|
|
1820
|
-
read: (
|
|
1821
|
-
listDir: (
|
|
1807
|
+
grep: (pattern, path6, glob) => post(`${baseUrl}/api/code-search/${encodedRepoId}/grep`, { pattern, path: path6, glob, branch }, "grep"),
|
|
1808
|
+
read: (path6, start, end) => post(`${baseUrl}/api/code-search/${encodedRepoId}/read`, { path: path6, start, end, branch }, "read"),
|
|
1809
|
+
listDir: (path6, maxDepth) => post(`${baseUrl}/api/code-search/${encodedRepoId}/list`, { path: path6, maxDepth, branch }, "list")
|
|
1822
1810
|
};
|
|
1823
1811
|
}
|
|
1824
1812
|
|
|
@@ -2113,7 +2101,7 @@ function formatResult(result) {
|
|
|
2113
2101
|
var WARP_GREP_TOOL_NAME = "codebase_search";
|
|
2114
2102
|
var WARP_GREP_DESCRIPTION = 'Very fast code search exploration subagent (not a grep tool) that runs parallel grep and file read calls over multiple turns to locate relevant files and line ranges. The search term should be a targeted natural-language query describing what you are trying to find or accomplish, e.g. "Find where authentication requests are handled in the Express routes" or "How do callers of processOrder handle the error case?". Fill in extra context you can infer to make the query specific. Do not pass bare keywords or symbol names \u2014 use grep directly for exact symbol lookups. Use this tool first when exploring unfamiliar code. The results may be partial \u2014 follow up with classical search tools or direct file reads if needed.';
|
|
2115
2103
|
var GITHUB_WARP_GREP_SEARCH_TOOL_NAME = "github_codebase_search";
|
|
2116
|
-
var GITHUB_WARP_GREP_SEARCH_DESCRIPTION = "Code search exploration subagent for public GitHub repositories (not your local workspace \u2014 use codebase_search for that). Runs parallel grep and file read calls to locate relevant files and line ranges in external github repositories without cloning the repository. Pass a targeted natural-language query, not bare keywords. Useful for understanding how a library works or investigating upstream bugs. You must provide either github_url or owner_repo (at least one required). ";
|
|
2104
|
+
var GITHUB_WARP_GREP_SEARCH_DESCRIPTION = "Use when you need to understand how a dependency or open-source project works by reading its actual source code on GitHub. Code search exploration subagent for public GitHub repositories (not your local workspace \u2014 use codebase_search for that). Runs parallel grep and file read calls to locate relevant files and line ranges in external github repositories without cloning the repository. Pass a targeted natural-language query, not bare keywords. Useful for understanding how a library works or investigating upstream bugs. You must provide either github_url or owner_repo (at least one required). ";
|
|
2117
2105
|
var GITHUB_WARP_GREP_SEARCH_INPUT_SCHEMA = {
|
|
2118
2106
|
type: "object",
|
|
2119
2107
|
properties: {
|
|
@@ -2150,6 +2138,25 @@ init_paths();
|
|
|
2150
2138
|
// tools/warp_grep/agent/index.ts
|
|
2151
2139
|
init_config();
|
|
2152
2140
|
|
|
2141
|
+
// tools/warp_grep/agent/formatter.ts
|
|
2142
|
+
var ToolOutputFormatter = class {
|
|
2143
|
+
format(toolName, _args, output, options = {}) {
|
|
2144
|
+
const name = (toolName ?? "").trim();
|
|
2145
|
+
if (!name) {
|
|
2146
|
+
return "";
|
|
2147
|
+
}
|
|
2148
|
+
const payload = output?.toString?.()?.trim?.() ?? "";
|
|
2149
|
+
const isError = Boolean(options.isError);
|
|
2150
|
+
if (!payload && !isError) {
|
|
2151
|
+
return "";
|
|
2152
|
+
}
|
|
2153
|
+
return `<tool_response>
|
|
2154
|
+
${payload}
|
|
2155
|
+
</tool_response>`;
|
|
2156
|
+
}
|
|
2157
|
+
};
|
|
2158
|
+
var sharedFormatter = new ToolOutputFormatter();
|
|
2159
|
+
|
|
2153
2160
|
// tools/warp_grep/index.ts
|
|
2154
2161
|
var warpGrepInputSchema = import_zod.z.object({
|
|
2155
2162
|
search_term: import_zod.z.string().describe("Search problem statement that this subagent is supposed to research for")
|