@morphllm/morphsdk 0.2.145 → 0.2.146
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-HBIW2XV2.js → chunk-4PBUB77N.js} +2 -2
- package/dist/{chunk-SUE4GYA2.js → chunk-BDHKL3MT.js} +2 -2
- package/dist/{chunk-S54SPKX3.js → chunk-BIQ7234U.js} +2 -2
- package/dist/{chunk-I3J46TSB.js → chunk-DKODF3YG.js} +4 -5
- package/dist/chunk-DKODF3YG.js.map +1 -0
- package/dist/{chunk-MRPASJBX.js → chunk-E45FW5EK.js} +2 -2
- package/dist/{chunk-BXRJYLRS.js → chunk-E4YKEKGW.js} +2 -2
- package/dist/{chunk-G23BI5CQ.js → chunk-EU7OLX4Z.js} +2 -2
- package/dist/chunk-EUHNJMWL.js +409 -0
- package/dist/chunk-EUHNJMWL.js.map +1 -0
- package/dist/{chunk-HE7K2QNQ.js → chunk-FBOJJ3UY.js} +17 -17
- package/dist/{chunk-HYRHI2UL.js → chunk-FIVYDIHX.js} +1 -1
- package/dist/{chunk-GXM3G7Z4.js → chunk-FYO46OT6.js} +2 -2
- package/dist/{chunk-GHPQYSSF.js → chunk-GJUB3ECP.js} +2 -2
- package/dist/{chunk-4Y2NM6JD.js → chunk-HZOTLGJH.js} +2 -42
- package/dist/chunk-HZOTLGJH.js.map +1 -0
- package/dist/{chunk-MTJ3PR4M.js → chunk-I7SFRYTX.js} +2 -2
- package/dist/{chunk-PX7ODEML.js → chunk-J2HIK4GB.js} +2 -2
- package/dist/{chunk-RZXS4ADX.js → chunk-JSWNBCGS.js} +2 -2
- package/dist/{chunk-GXCWKYGU.js → chunk-KYKRRF7E.js} +2 -2
- package/dist/{chunk-N7TTZIBK.js → chunk-MMBQKN4G.js} +2 -2
- package/dist/{chunk-B3AKP3RA.js → chunk-NF2QWJDY.js} +2 -31
- package/dist/chunk-NF2QWJDY.js.map +1 -0
- package/dist/{chunk-JMUAQQJU.js → chunk-NKUSUSVI.js} +3 -3
- package/dist/{chunk-VRV5UYTN.js → chunk-OV57JBMB.js} +2 -2
- package/dist/{chunk-EPIOAODF.js → chunk-Q36MNOFA.js} +2 -2
- package/dist/{chunk-JRBU4UNP.js → chunk-QRSWXP4K.js} +2 -2
- package/dist/{chunk-KELRCMA6.js → chunk-SJYAKVSS.js} +2 -2
- package/dist/{chunk-KELRCMA6.js.map → chunk-SJYAKVSS.js.map} +1 -1
- package/dist/{chunk-IRWHN55G.js → chunk-T564HFSH.js} +1 -1
- package/dist/{chunk-6CFKWZK3.js → chunk-UVNENJ6H.js} +3 -3
- package/dist/{chunk-5FCXLQJU.js → chunk-UYPWKQKV.js} +2 -2
- package/dist/{chunk-BAF33L6C.js → chunk-V73GO5AJ.js} +2 -2
- package/dist/chunk-VCKJ22DX.js +131 -0
- package/dist/chunk-VCKJ22DX.js.map +1 -0
- package/dist/{chunk-XL7R3XN5.js → chunk-VZ6VYRQB.js} +2 -2
- package/dist/{chunk-4LWMPKSB.js → chunk-YIETFYCL.js} +44 -71
- package/dist/chunk-YIETFYCL.js.map +1 -0
- package/dist/client.cjs +438 -426
- package/dist/client.cjs.map +1 -1
- package/dist/client.js +27 -26
- package/dist/edge.cjs +1 -1
- package/dist/edge.cjs.map +1 -1
- package/dist/edge.js +4 -4
- package/dist/{finish-Ddj1MPGt.d.ts → finish-DBKuo8yj.d.ts} +1 -1
- package/dist/index.cjs +438 -445
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +29 -29
- 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 +434 -422
- package/dist/subagents/anthropic.cjs.map +1 -1
- package/dist/subagents/anthropic.js +9 -8
- package/dist/subagents/vercel.cjs +434 -422
- package/dist/subagents/vercel.cjs.map +1 -1
- package/dist/subagents/vercel.js +9 -8
- 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 +3 -4
- package/dist/tools/warp_grep/agent/config.cjs.map +1 -1
- package/dist/tools/warp_grep/agent/config.d.ts +1 -2
- package/dist/tools/warp_grep/agent/config.js +1 -1
- package/dist/tools/warp_grep/agent/parser.cjs +121 -52
- package/dist/tools/warp_grep/agent/parser.cjs.map +1 -1
- package/dist/tools/warp_grep/agent/parser.d.ts +5 -12
- package/dist/tools/warp_grep/agent/parser.js +3 -7
- package/dist/tools/warp_grep/agent/runner.cjs +416 -335
- package/dist/tools/warp_grep/agent/runner.cjs.map +1 -1
- package/dist/tools/warp_grep/agent/runner.d.ts +3 -6
- package/dist/tools/warp_grep/agent/runner.js +6 -5
- package/dist/tools/warp_grep/agent/types.cjs.map +1 -1
- package/dist/tools/warp_grep/agent/types.d.ts +3 -22
- package/dist/tools/warp_grep/anthropic.cjs +434 -422
- package/dist/tools/warp_grep/anthropic.cjs.map +1 -1
- package/dist/tools/warp_grep/anthropic.js +9 -8
- package/dist/tools/warp_grep/client.cjs +434 -422
- package/dist/tools/warp_grep/client.cjs.map +1 -1
- package/dist/tools/warp_grep/client.js +8 -7
- package/dist/tools/warp_grep/gemini.cjs +434 -422
- package/dist/tools/warp_grep/gemini.cjs.map +1 -1
- package/dist/tools/warp_grep/gemini.js +8 -7
- package/dist/tools/warp_grep/gemini.js.map +1 -1
- package/dist/tools/warp_grep/harness.cjs +176 -164
- package/dist/tools/warp_grep/harness.cjs.map +1 -1
- package/dist/tools/warp_grep/harness.d.ts +38 -17
- package/dist/tools/warp_grep/harness.js +14 -15
- package/dist/tools/warp_grep/harness.js.map +1 -1
- package/dist/tools/warp_grep/index.cjs +434 -441
- 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 +10 -10
- package/dist/tools/warp_grep/openai.cjs +434 -422
- package/dist/tools/warp_grep/openai.cjs.map +1 -1
- package/dist/tools/warp_grep/openai.js +9 -8
- package/dist/tools/warp_grep/providers/local.cjs +2 -43
- package/dist/tools/warp_grep/providers/local.cjs.map +1 -1
- package/dist/tools/warp_grep/providers/local.d.ts +1 -5
- package/dist/tools/warp_grep/providers/local.js +2 -2
- package/dist/tools/warp_grep/providers/remote.cjs +2 -32
- package/dist/tools/warp_grep/providers/remote.cjs.map +1 -1
- package/dist/tools/warp_grep/providers/remote.d.ts +1 -9
- 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 +1 -14
- package/dist/tools/warp_grep/vercel.cjs +434 -422
- package/dist/tools/warp_grep/vercel.cjs.map +1 -1
- package/dist/tools/warp_grep/vercel.js +9 -8
- 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-4LWMPKSB.js.map +0 -1
- package/dist/chunk-4Y2NM6JD.js.map +0 -1
- package/dist/chunk-B3AKP3RA.js.map +0 -1
- package/dist/chunk-CMSHXALI.js +0 -60
- package/dist/chunk-CMSHXALI.js.map +0 -1
- package/dist/chunk-I3J46TSB.js.map +0 -1
- package/dist/chunk-OPEQQGST.js +0 -396
- package/dist/chunk-OPEQQGST.js.map +0 -1
- /package/dist/{chunk-HBIW2XV2.js.map → chunk-4PBUB77N.js.map} +0 -0
- /package/dist/{chunk-SUE4GYA2.js.map → chunk-BDHKL3MT.js.map} +0 -0
- /package/dist/{chunk-S54SPKX3.js.map → chunk-BIQ7234U.js.map} +0 -0
- /package/dist/{chunk-MRPASJBX.js.map → chunk-E45FW5EK.js.map} +0 -0
- /package/dist/{chunk-BXRJYLRS.js.map → chunk-E4YKEKGW.js.map} +0 -0
- /package/dist/{chunk-G23BI5CQ.js.map → chunk-EU7OLX4Z.js.map} +0 -0
- /package/dist/{chunk-HE7K2QNQ.js.map → chunk-FBOJJ3UY.js.map} +0 -0
- /package/dist/{chunk-HYRHI2UL.js.map → chunk-FIVYDIHX.js.map} +0 -0
- /package/dist/{chunk-GXM3G7Z4.js.map → chunk-FYO46OT6.js.map} +0 -0
- /package/dist/{chunk-GHPQYSSF.js.map → chunk-GJUB3ECP.js.map} +0 -0
- /package/dist/{chunk-MTJ3PR4M.js.map → chunk-I7SFRYTX.js.map} +0 -0
- /package/dist/{chunk-PX7ODEML.js.map → chunk-J2HIK4GB.js.map} +0 -0
- /package/dist/{chunk-RZXS4ADX.js.map → chunk-JSWNBCGS.js.map} +0 -0
- /package/dist/{chunk-GXCWKYGU.js.map → chunk-KYKRRF7E.js.map} +0 -0
- /package/dist/{chunk-N7TTZIBK.js.map → chunk-MMBQKN4G.js.map} +0 -0
- /package/dist/{chunk-JMUAQQJU.js.map → chunk-NKUSUSVI.js.map} +0 -0
- /package/dist/{chunk-VRV5UYTN.js.map → chunk-OV57JBMB.js.map} +0 -0
- /package/dist/{chunk-EPIOAODF.js.map → chunk-Q36MNOFA.js.map} +0 -0
- /package/dist/{chunk-JRBU4UNP.js.map → chunk-QRSWXP4K.js.map} +0 -0
- /package/dist/{chunk-IRWHN55G.js.map → chunk-T564HFSH.js.map} +0 -0
- /package/dist/{chunk-6CFKWZK3.js.map → chunk-UVNENJ6H.js.map} +0 -0
- /package/dist/{chunk-5FCXLQJU.js.map → chunk-UYPWKQKV.js.map} +0 -0
- /package/dist/{chunk-BAF33L6C.js.map → chunk-V73GO5AJ.js.map} +0 -0
- /package/dist/{chunk-XL7R3XN5.js.map → chunk-VZ6VYRQB.js.map} +0 -0
|
@@ -41,12 +41,11 @@ 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: 4,
|
|
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: 54e4,
|
|
48
48
|
MAX_OUTPUT_LINES: 200,
|
|
49
|
-
MAX_LIST_RESULTS: 500,
|
|
50
49
|
MAX_READ_LINES: 800,
|
|
51
50
|
MAX_LIST_DEPTH: 3,
|
|
52
51
|
LIST_TIMEOUT_MS: 2e3
|
|
@@ -131,7 +130,7 @@ var init_config = __esm({
|
|
|
131
130
|
".*"
|
|
132
131
|
];
|
|
133
132
|
DEFAULT_EXCLUDES = (process.env.MORPH_WARP_GREP_EXCLUDE || "").split(",").map((s) => s.trim()).filter(Boolean).concat(BUILTIN_EXCLUDES);
|
|
134
|
-
DEFAULT_MODEL = "morph-warp-grep-v2
|
|
133
|
+
DEFAULT_MODEL = "morph-warp-grep-v2";
|
|
135
134
|
}
|
|
136
135
|
});
|
|
137
136
|
|
|
@@ -204,19 +203,19 @@ var init_ripgrep = __esm({
|
|
|
204
203
|
|
|
205
204
|
// tools/warp_grep/utils/paths.ts
|
|
206
205
|
function resolveUnderRepo(repoRoot, targetPath) {
|
|
207
|
-
const absRoot =
|
|
208
|
-
const resolved =
|
|
206
|
+
const absRoot = import_path3.default.resolve(repoRoot);
|
|
207
|
+
const resolved = import_path3.default.resolve(absRoot, targetPath);
|
|
209
208
|
ensureWithinRepo(absRoot, resolved);
|
|
210
209
|
return resolved;
|
|
211
210
|
}
|
|
212
211
|
function ensureWithinRepo(repoRoot, absTarget) {
|
|
213
|
-
const rel =
|
|
214
|
-
if (rel.startsWith("..") ||
|
|
212
|
+
const rel = import_path3.default.relative(import_path3.default.resolve(repoRoot), import_path3.default.resolve(absTarget));
|
|
213
|
+
if (rel.startsWith("..") || import_path3.default.isAbsolute(rel)) {
|
|
215
214
|
throw new Error(`Path outside repository root: ${absTarget}`);
|
|
216
215
|
}
|
|
217
216
|
}
|
|
218
217
|
function toRepoRelative(repoRoot, absPath) {
|
|
219
|
-
return
|
|
218
|
+
return import_path3.default.relative(import_path3.default.resolve(repoRoot), import_path3.default.resolve(absPath));
|
|
220
219
|
}
|
|
221
220
|
function isSymlink(p) {
|
|
222
221
|
try {
|
|
@@ -227,7 +226,7 @@ function isSymlink(p) {
|
|
|
227
226
|
}
|
|
228
227
|
}
|
|
229
228
|
function fixPathRepetition(fullPath) {
|
|
230
|
-
const segments = fullPath.split(
|
|
229
|
+
const segments = fullPath.split(import_path3.default.sep).filter(Boolean);
|
|
231
230
|
if (segments.length < 2) return null;
|
|
232
231
|
for (let len = Math.floor(segments.length / 2); len >= 1; len--) {
|
|
233
232
|
for (let i = 0; i <= segments.length - 2 * len; i++) {
|
|
@@ -235,7 +234,7 @@ function fixPathRepetition(fullPath) {
|
|
|
235
234
|
const second = segments.slice(i + len, i + 2 * len);
|
|
236
235
|
if (first.every((seg, idx) => seg === second[idx])) {
|
|
237
236
|
const fixed = [...segments.slice(0, i), ...segments.slice(i + len)];
|
|
238
|
-
return
|
|
237
|
+
return import_path3.default.sep + fixed.join(import_path3.default.sep);
|
|
239
238
|
}
|
|
240
239
|
}
|
|
241
240
|
}
|
|
@@ -259,12 +258,12 @@ function isTextualFile(filePath, maxBytes = 2e6) {
|
|
|
259
258
|
return false;
|
|
260
259
|
}
|
|
261
260
|
}
|
|
262
|
-
var import_fs,
|
|
261
|
+
var import_fs, import_path3;
|
|
263
262
|
var init_paths = __esm({
|
|
264
263
|
"tools/warp_grep/utils/paths.ts"() {
|
|
265
264
|
"use strict";
|
|
266
265
|
import_fs = __toESM(require("fs"), 1);
|
|
267
|
-
|
|
266
|
+
import_path3 = __toESM(require("path"), 1);
|
|
268
267
|
}
|
|
269
268
|
});
|
|
270
269
|
|
|
@@ -295,12 +294,12 @@ function shouldSkip2(name, allowNames) {
|
|
|
295
294
|
}
|
|
296
295
|
return false;
|
|
297
296
|
}
|
|
298
|
-
var import_promises2,
|
|
297
|
+
var import_promises2, import_path4, SKIP_NAMES2, SKIP_EXTENSIONS2, LocalRipgrepProvider;
|
|
299
298
|
var init_local = __esm({
|
|
300
299
|
"tools/warp_grep/providers/local.ts"() {
|
|
301
300
|
"use strict";
|
|
302
301
|
import_promises2 = __toESM(require("fs/promises"), 1);
|
|
303
|
-
|
|
302
|
+
import_path4 = __toESM(require("path"), 1);
|
|
304
303
|
init_ripgrep();
|
|
305
304
|
init_paths();
|
|
306
305
|
init_files();
|
|
@@ -402,7 +401,7 @@ var init_local = __esm({
|
|
|
402
401
|
}
|
|
403
402
|
const stat = await import_promises2.default.stat(abs).catch(() => null);
|
|
404
403
|
if (!stat) return { lines: [] };
|
|
405
|
-
const targetArg = abs ===
|
|
404
|
+
const targetArg = abs === import_path4.default.resolve(this.repoRoot) ? "." : toRepoRelative(this.repoRoot, abs);
|
|
406
405
|
const contextLines = params.context_lines !== void 0 ? String(params.context_lines) : "1";
|
|
407
406
|
const args = [
|
|
408
407
|
"--no-config",
|
|
@@ -557,7 +556,7 @@ Details: ${res.stderr}` : ""}`
|
|
|
557
556
|
if (timedOut || results.length >= maxResults) break;
|
|
558
557
|
if (shouldSkip2(entry.name, allowNames)) continue;
|
|
559
558
|
if (regex && !regex.test(entry.name)) continue;
|
|
560
|
-
const full =
|
|
559
|
+
const full = import_path4.default.join(dir, entry.name);
|
|
561
560
|
const isDir = entry.isDirectory();
|
|
562
561
|
results.push({
|
|
563
562
|
name: entry.name,
|
|
@@ -573,46 +572,6 @@ Details: ${res.stderr}` : ""}`
|
|
|
573
572
|
await walk(abs, 0);
|
|
574
573
|
return results;
|
|
575
574
|
}
|
|
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
|
-
}
|
|
616
575
|
};
|
|
617
576
|
}
|
|
618
577
|
});
|
|
@@ -654,58 +613,131 @@ var import_zod = require("zod");
|
|
|
654
613
|
init_config();
|
|
655
614
|
|
|
656
615
|
// tools/warp_grep/agent/parser.ts
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
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 {};
|
|
616
|
+
var VALID_COMMANDS = ["list_directory", "ripgrep", "read", "finish"];
|
|
617
|
+
function isValidCommand(name) {
|
|
618
|
+
return VALID_COMMANDS.includes(name);
|
|
672
619
|
}
|
|
673
|
-
function
|
|
674
|
-
const
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
const
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
files.push({ path: filePath, lines: "*" });
|
|
687
|
-
continue;
|
|
620
|
+
function parseQwen3ToolCalls(text) {
|
|
621
|
+
const tools = [];
|
|
622
|
+
const toolCallRegex = /<tool_call>\s*<function=([a-z_][a-z0-9_]*)>([\s\S]*?)<\/function>\s*<\/tool_call>/gi;
|
|
623
|
+
let match;
|
|
624
|
+
while ((match = toolCallRegex.exec(text)) !== null) {
|
|
625
|
+
const funcName = match[1].toLowerCase();
|
|
626
|
+
const body = match[2];
|
|
627
|
+
if (!isValidCommand(funcName)) continue;
|
|
628
|
+
const params = {};
|
|
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();
|
|
688
633
|
}
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
634
|
+
if (funcName === "ripgrep") {
|
|
635
|
+
const pattern = params.pattern;
|
|
636
|
+
if (!pattern) continue;
|
|
637
|
+
const args = {
|
|
638
|
+
pattern,
|
|
639
|
+
path: params.path || ".",
|
|
640
|
+
...params.glob && { glob: params.glob },
|
|
641
|
+
...params.context_lines && { context_lines: parseInt(params.context_lines, 10) },
|
|
642
|
+
...params.case_sensitive && { case_sensitive: params.case_sensitive === "true" }
|
|
643
|
+
};
|
|
644
|
+
tools.push({ name: "grep", arguments: args });
|
|
645
|
+
} else if (funcName === "list_directory") {
|
|
646
|
+
const command = params.command;
|
|
647
|
+
const directPath = params.path;
|
|
648
|
+
let dirPath = directPath || ".";
|
|
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 } });
|
|
698
727
|
}
|
|
699
728
|
}
|
|
700
|
-
files.push({ path: filePath, lines: ranges.length > 0 ? ranges : "*" });
|
|
701
729
|
}
|
|
702
|
-
return
|
|
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] || ".";
|
|
730
|
+
return tools;
|
|
708
731
|
}
|
|
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
|
+
};
|
|
709
741
|
|
|
710
742
|
// tools/warp_grep/agent/tools/grep.ts
|
|
711
743
|
async function toolGrep(provider, args) {
|
|
@@ -756,42 +788,29 @@ async function toolRead(provider, args) {
|
|
|
756
788
|
|
|
757
789
|
// tools/warp_grep/agent/tools/list_directory.ts
|
|
758
790
|
init_config();
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
const
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
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}`;
|
|
791
|
+
async function toolListDirectory(provider, args) {
|
|
792
|
+
const maxResults = args.maxResults ?? AGENT_CONFIG.MAX_OUTPUT_LINES;
|
|
793
|
+
const initialDepth = args.maxDepth ?? AGENT_CONFIG.MAX_LIST_DEPTH;
|
|
794
|
+
async function getListRecursive(currentDepth) {
|
|
795
|
+
const entries = await provider.listDirectory({
|
|
796
|
+
path: args.path,
|
|
797
|
+
pattern: args.pattern ?? null,
|
|
798
|
+
maxResults,
|
|
799
|
+
maxDepth: currentDepth
|
|
800
|
+
});
|
|
801
|
+
if (entries.length >= maxResults && currentDepth > 0) {
|
|
802
|
+
return getListRecursive(currentDepth - 1);
|
|
803
|
+
}
|
|
804
|
+
return { entries };
|
|
805
|
+
}
|
|
806
|
+
const { entries: list } = await getListRecursive(initialDepth);
|
|
807
|
+
if (!list.length) return "empty";
|
|
808
|
+
const tree = list.map((e) => {
|
|
809
|
+
const indent = " ".repeat(e.depth);
|
|
810
|
+
const name = e.type === "dir" ? `${e.name}/` : e.name;
|
|
811
|
+
return `${indent}${name}`;
|
|
812
|
+
}).join("\n");
|
|
813
|
+
return tree;
|
|
795
814
|
}
|
|
796
815
|
|
|
797
816
|
// tools/warp_grep/agent/tools/finish.ts
|
|
@@ -860,21 +879,32 @@ function mergeRanges(ranges) {
|
|
|
860
879
|
return merged;
|
|
861
880
|
}
|
|
862
881
|
|
|
863
|
-
// tools/warp_grep/agent/
|
|
864
|
-
var
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
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);
|
|
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 "";
|
|
873
888
|
}
|
|
874
|
-
|
|
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>`;
|
|
875
897
|
}
|
|
876
|
-
|
|
898
|
+
};
|
|
899
|
+
var sharedFormatter = new ToolOutputFormatter();
|
|
900
|
+
function formatAgentToolOutput(toolName, args, output, options = {}) {
|
|
901
|
+
return sharedFormatter.format(toolName, args, output, options);
|
|
877
902
|
}
|
|
903
|
+
|
|
904
|
+
// tools/warp_grep/agent/helpers.ts
|
|
905
|
+
var import_path = __toESM(require("path"), 1);
|
|
906
|
+
init_config();
|
|
907
|
+
var TRUNCATED_MARKER = "[truncated for context limit]";
|
|
878
908
|
function formatTurnMessage(turnsUsed, maxTurns) {
|
|
879
909
|
const turnsRemaining = maxTurns - turnsUsed;
|
|
880
910
|
if (turnsRemaining === 1) {
|
|
@@ -885,7 +915,7 @@ You have used ${turnsUsed} turns, you only have 1 turn remaining. You have run o
|
|
|
885
915
|
You have used ${turnsUsed} turn${turnsUsed === 1 ? "" : "s"} and have ${turnsRemaining} remaining`;
|
|
886
916
|
}
|
|
887
917
|
function calculateContextBudget(messages) {
|
|
888
|
-
const totalChars = messages.reduce((sum, m) => sum +
|
|
918
|
+
const totalChars = messages.reduce((sum, m) => sum + m.content.length, 0);
|
|
889
919
|
const maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS;
|
|
890
920
|
const percent = Math.round(totalChars / maxChars * 100);
|
|
891
921
|
const usedK = Math.round(totalChars / 1e3);
|
|
@@ -894,21 +924,24 @@ function calculateContextBudget(messages) {
|
|
|
894
924
|
}
|
|
895
925
|
async function buildInitialState(repoRoot, searchTerm, provider, options) {
|
|
896
926
|
const budget = calculateContextBudget([]);
|
|
897
|
-
const turnTag = `
|
|
927
|
+
const turnTag = `Turn 0/${AGENT_CONFIG.MAX_TURNS}`;
|
|
898
928
|
const treeDepth = options?.search_type === "node_modules" ? 1 : 2;
|
|
899
|
-
const absRoot = import_path2.default.resolve(repoRoot);
|
|
900
929
|
try {
|
|
901
930
|
const entries = await provider.listDirectory({
|
|
902
931
|
path: ".",
|
|
903
932
|
maxResults: AGENT_CONFIG.MAX_OUTPUT_LINES,
|
|
904
933
|
maxDepth: treeDepth
|
|
905
934
|
});
|
|
906
|
-
const
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
935
|
+
const treeLines = entries.map((e) => {
|
|
936
|
+
const indent = " ".repeat(e.depth);
|
|
937
|
+
const name = e.type === "dir" ? `${e.name}/` : e.name;
|
|
938
|
+
return `${indent}${name}`;
|
|
939
|
+
});
|
|
940
|
+
const repoName = import_path.default.basename(repoRoot);
|
|
941
|
+
const treeOutput = treeLines.length > 0 ? `${repoName}/
|
|
942
|
+
${treeLines.join("\n")}` : `${repoName}/`;
|
|
910
943
|
return `<repo_structure>
|
|
911
|
-
${
|
|
944
|
+
${treeOutput}
|
|
912
945
|
</repo_structure>
|
|
913
946
|
|
|
914
947
|
<search_string>
|
|
@@ -917,8 +950,9 @@ ${searchTerm}
|
|
|
917
950
|
${budget}
|
|
918
951
|
${turnTag}`;
|
|
919
952
|
} catch {
|
|
953
|
+
const repoName = import_path.default.basename(repoRoot);
|
|
920
954
|
return `<repo_structure>
|
|
921
|
-
${
|
|
955
|
+
${repoName}/
|
|
922
956
|
</repo_structure>
|
|
923
957
|
|
|
924
958
|
<search_string>
|
|
@@ -929,32 +963,26 @@ ${turnTag}`;
|
|
|
929
963
|
}
|
|
930
964
|
}
|
|
931
965
|
function enforceContextLimit(messages, maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS) {
|
|
932
|
-
const getTotalChars = () => messages.reduce((sum, m) => sum +
|
|
966
|
+
const getTotalChars = () => messages.reduce((sum, m) => sum + m.content.length, 0);
|
|
933
967
|
if (getTotalChars() <= maxChars) {
|
|
934
968
|
return messages;
|
|
935
969
|
}
|
|
936
|
-
const
|
|
970
|
+
const userIndices = [];
|
|
937
971
|
let firstUserSkipped = false;
|
|
938
972
|
for (let i = 0; i < messages.length; i++) {
|
|
939
|
-
|
|
940
|
-
if (m.role === "tool") {
|
|
941
|
-
truncatableIndices.push(i);
|
|
942
|
-
} else if (m.role === "user") {
|
|
973
|
+
if (messages[i].role === "user") {
|
|
943
974
|
if (!firstUserSkipped) {
|
|
944
975
|
firstUserSkipped = true;
|
|
945
976
|
continue;
|
|
946
977
|
}
|
|
947
|
-
|
|
978
|
+
userIndices.push(i);
|
|
948
979
|
}
|
|
949
980
|
}
|
|
950
|
-
for (const idx of
|
|
981
|
+
for (const idx of userIndices) {
|
|
951
982
|
if (getTotalChars() <= maxChars) {
|
|
952
983
|
break;
|
|
953
984
|
}
|
|
954
|
-
|
|
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) {
|
|
985
|
+
if (messages[idx].content !== TRUNCATED_MARKER) {
|
|
958
986
|
messages[idx] = { role: "user", content: TRUNCATED_MARKER };
|
|
959
987
|
}
|
|
960
988
|
}
|
|
@@ -967,7 +995,7 @@ var import_openai = __toESM(require("openai"), 1);
|
|
|
967
995
|
// package.json
|
|
968
996
|
var package_default = {
|
|
969
997
|
name: "@morphllm/morphsdk",
|
|
970
|
-
version: "0.2.
|
|
998
|
+
version: "0.2.146",
|
|
971
999
|
description: "TypeScript SDK and CLI for Morph Fast Apply integration",
|
|
972
1000
|
type: "module",
|
|
973
1001
|
main: "./dist/index.cjs",
|
|
@@ -1206,115 +1234,9 @@ var package_default = {
|
|
|
1206
1234
|
var SDK_VERSION = package_default.version;
|
|
1207
1235
|
|
|
1208
1236
|
// tools/warp_grep/agent/runner.ts
|
|
1209
|
-
var
|
|
1237
|
+
var import_path2 = __toESM(require("path"), 1);
|
|
1238
|
+
var parser = new LLMResponseParser();
|
|
1210
1239
|
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
|
-
];
|
|
1318
1240
|
async function callModel(messages, model, options = {}) {
|
|
1319
1241
|
const baseUrl = options.morphApiUrl || DEFAULT_API_URL;
|
|
1320
1242
|
const apiKey = options.morphApiKey || process.env.MORPH_API_KEY || "";
|
|
@@ -1335,9 +1257,8 @@ async function callModel(messages, model, options = {}) {
|
|
|
1335
1257
|
data = await client.chat.completions.create({
|
|
1336
1258
|
model,
|
|
1337
1259
|
temperature: 0,
|
|
1338
|
-
max_tokens:
|
|
1260
|
+
max_tokens: 1024,
|
|
1339
1261
|
messages,
|
|
1340
|
-
tools: TOOL_SPECS,
|
|
1341
1262
|
...options.search_type ? { search_type: options.search_type } : {}
|
|
1342
1263
|
});
|
|
1343
1264
|
} catch (error) {
|
|
@@ -1349,87 +1270,187 @@ async function callModel(messages, model, options = {}) {
|
|
|
1349
1270
|
throw error;
|
|
1350
1271
|
}
|
|
1351
1272
|
const choice = data?.choices?.[0];
|
|
1352
|
-
const
|
|
1353
|
-
if (
|
|
1354
|
-
|
|
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 };
|
|
1273
|
+
const content = choice?.message?.content;
|
|
1274
|
+
if (content && typeof content === "string") {
|
|
1275
|
+
return content;
|
|
1367
1276
|
}
|
|
1368
1277
|
if (attempt === MAX_EMPTY_RETRIES) {
|
|
1369
1278
|
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;
|
|
1370
1282
|
throw new Error(
|
|
1371
|
-
`Invalid response from model:
|
|
1283
|
+
`Invalid response from model: content=${contentType}, finish_reason=${finishReason}, has_tool_calls=${hasToolCalls}, choices_length=${choicesLen}`
|
|
1372
1284
|
);
|
|
1373
1285
|
}
|
|
1374
1286
|
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
1375
1287
|
}
|
|
1376
1288
|
throw new Error("Invalid response from model");
|
|
1377
1289
|
}
|
|
1378
|
-
function
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1290
|
+
async function runWarpGrep(config) {
|
|
1291
|
+
const totalStart = Date.now();
|
|
1292
|
+
const timeoutMs = config.timeout ?? AGENT_CONFIG.TIMEOUT_MS;
|
|
1293
|
+
const timings = { turns: [], timeout_ms: timeoutMs };
|
|
1294
|
+
const repoRoot = import_path2.default.resolve(config.repoRoot || process.cwd());
|
|
1295
|
+
const model = config.model || DEFAULT_MODEL;
|
|
1296
|
+
const messages = [];
|
|
1297
|
+
const maxTurns = AGENT_CONFIG.MAX_TURNS;
|
|
1298
|
+
const initialStateStart = Date.now();
|
|
1299
|
+
const initialState = await buildInitialState(repoRoot, config.searchTerm, config.provider, { search_type: config.search_type });
|
|
1300
|
+
timings.initial_state_ms = Date.now() - initialStateStart;
|
|
1301
|
+
messages.push({ role: "user", content: initialState });
|
|
1302
|
+
const provider = config.provider;
|
|
1303
|
+
const errors = [];
|
|
1304
|
+
let finishMeta;
|
|
1305
|
+
let terminationReason = "terminated";
|
|
1306
|
+
for (let turn = 1; turn <= maxTurns; turn += 1) {
|
|
1307
|
+
const turnMetrics = { turn, morph_api_ms: 0, local_tools_ms: 0 };
|
|
1308
|
+
enforceContextLimit(messages);
|
|
1309
|
+
const modelCallStart = Date.now();
|
|
1310
|
+
const assistantContent = await callModel(messages, model, {
|
|
1311
|
+
morphApiKey: config.morphApiKey,
|
|
1312
|
+
morphApiUrl: config.morphApiUrl,
|
|
1313
|
+
retryConfig: config.retryConfig,
|
|
1314
|
+
timeout: timeoutMs,
|
|
1315
|
+
search_type: config.search_type
|
|
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;
|
|
1404
1327
|
}
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
});
|
|
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;
|
|
1410
1336
|
}
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
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);
|
|
1414
1346
|
}
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
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);
|
|
1380
|
+
}
|
|
1381
|
+
if (formatted.length > 0) {
|
|
1382
|
+
const turnMessage = formatTurnMessage(turn, maxTurns);
|
|
1383
|
+
const contextBudget = calculateContextBudget(messages);
|
|
1384
|
+
messages.push({ role: "user", content: formatted.join("\n") + turnMessage + "\n" + contextBudget });
|
|
1385
|
+
}
|
|
1386
|
+
timings.turns.push(turnMetrics);
|
|
1387
|
+
if (finishCalls.length) {
|
|
1388
|
+
const fc = finishCalls[0];
|
|
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;
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
if (terminationReason !== "completed" || !finishMeta) {
|
|
1408
|
+
timings.total_ms = Date.now() - totalStart;
|
|
1409
|
+
return { terminationReason, messages, errors, timings };
|
|
1410
|
+
}
|
|
1411
|
+
const parts = ["Relevant context found:"];
|
|
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}]`];
|
|
1421
1434
|
}
|
|
1422
|
-
return toolRead(provider, readArgs);
|
|
1423
1435
|
}
|
|
1424
|
-
|
|
1425
|
-
|
|
1436
|
+
);
|
|
1437
|
+
timings.finish_resolution_ms = Date.now() - finishResolutionStart;
|
|
1438
|
+
if (fileReadErrors.length > 0) {
|
|
1439
|
+
errors.push(...fileReadErrors.map((e) => ({ message: `File read error: ${e.path} - ${e.error}` })));
|
|
1426
1440
|
}
|
|
1441
|
+
timings.total_ms = Date.now() - totalStart;
|
|
1442
|
+
return {
|
|
1443
|
+
terminationReason: "completed",
|
|
1444
|
+
messages,
|
|
1445
|
+
finish: { payload, metadata: finishMeta, resolved },
|
|
1446
|
+
timings
|
|
1447
|
+
};
|
|
1427
1448
|
}
|
|
1428
1449
|
async function* runWarpGrepStreaming(config) {
|
|
1429
1450
|
const totalStart = Date.now();
|
|
1430
1451
|
const timeoutMs = config.timeout ?? AGENT_CONFIG.TIMEOUT_MS;
|
|
1431
1452
|
const timings = { turns: [], timeout_ms: timeoutMs };
|
|
1432
|
-
const repoRoot =
|
|
1453
|
+
const repoRoot = import_path2.default.resolve(config.repoRoot || process.cwd());
|
|
1433
1454
|
const model = config.model || DEFAULT_MODEL;
|
|
1434
1455
|
const messages = [];
|
|
1435
1456
|
const maxTurns = AGENT_CONFIG.MAX_TURNS;
|
|
@@ -1445,7 +1466,7 @@ async function* runWarpGrepStreaming(config) {
|
|
|
1445
1466
|
const turnMetrics = { turn, morph_api_ms: 0, local_tools_ms: 0 };
|
|
1446
1467
|
enforceContextLimit(messages);
|
|
1447
1468
|
const modelCallStart = Date.now();
|
|
1448
|
-
const
|
|
1469
|
+
const assistantContent = await callModel(messages, model, {
|
|
1449
1470
|
morphApiKey: config.morphApiKey,
|
|
1450
1471
|
morphApiUrl: config.morphApiUrl,
|
|
1451
1472
|
retryConfig: config.retryConfig,
|
|
@@ -1453,45 +1474,90 @@ async function* runWarpGrepStreaming(config) {
|
|
|
1453
1474
|
search_type: config.search_type
|
|
1454
1475
|
}).catch((e) => {
|
|
1455
1476
|
const errMsg = e instanceof Error ? e.message : String(e);
|
|
1456
|
-
console.error(`[warp_grep] Morph API call failed on turn ${turn}:`, errMsg);
|
|
1477
|
+
console.error(`[warp_grep:stream] Morph API call failed on turn ${turn}:`, errMsg);
|
|
1457
1478
|
errors.push({ message: errMsg });
|
|
1458
|
-
return
|
|
1479
|
+
return "";
|
|
1459
1480
|
});
|
|
1460
1481
|
turnMetrics.morph_api_ms = Date.now() - modelCallStart;
|
|
1461
|
-
if (!
|
|
1482
|
+
if (!assistantContent) {
|
|
1483
|
+
console.error(`[warp_grep:stream] Empty response from Morph API on turn ${turn}. Errors so far:`, errors);
|
|
1462
1484
|
timings.turns.push(turnMetrics);
|
|
1463
1485
|
break;
|
|
1464
1486
|
}
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
role: "assistant",
|
|
1468
|
-
content: response.content,
|
|
1469
|
-
...toolCalls.length > 0 ? { tool_calls: toolCalls } : {}
|
|
1470
|
-
});
|
|
1487
|
+
messages.push({ role: "assistant", content: assistantContent });
|
|
1488
|
+
const toolCalls = parser.parse(assistantContent);
|
|
1471
1489
|
if (toolCalls.length === 0) {
|
|
1472
|
-
console.error(`[warp_grep] No tool calls on turn ${turn}.
|
|
1473
|
-
errors.push({ message: "No tool calls produced by the model." });
|
|
1490
|
+
console.error(`[warp_grep:stream] No tool calls parsed on turn ${turn}. Assistant content (first 500 chars):`, assistantContent.slice(0, 500));
|
|
1491
|
+
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" });
|
|
1474
1492
|
terminationReason = "terminated";
|
|
1475
1493
|
timings.turns.push(turnMetrics);
|
|
1476
1494
|
break;
|
|
1477
1495
|
}
|
|
1478
1496
|
yield {
|
|
1479
1497
|
turn,
|
|
1480
|
-
toolCalls: toolCalls.map((
|
|
1481
|
-
name:
|
|
1482
|
-
arguments:
|
|
1498
|
+
toolCalls: toolCalls.map((c) => ({
|
|
1499
|
+
name: c.name,
|
|
1500
|
+
arguments: c.arguments ?? {}
|
|
1483
1501
|
}))
|
|
1484
1502
|
};
|
|
1485
|
-
const
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1503
|
+
const finishCalls = toolCalls.filter((c) => c.name === "finish");
|
|
1504
|
+
const grepCalls = toolCalls.filter((c) => c.name === "grep");
|
|
1505
|
+
const listDirCalls = toolCalls.filter((c) => c.name === "list_directory");
|
|
1506
|
+
const readCalls = toolCalls.filter((c) => c.name === "read");
|
|
1507
|
+
const skipCalls = toolCalls.filter((c) => c.name === "_skip");
|
|
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;
|
|
1490
1557
|
finishMeta = { files };
|
|
1491
1558
|
terminationReason = "completed";
|
|
1492
1559
|
if (files.length === 0) {
|
|
1493
|
-
const payload2 =
|
|
1494
|
-
timings.turns.push(turnMetrics);
|
|
1560
|
+
const payload2 = textResult || "No relevant code found.";
|
|
1495
1561
|
timings.total_ms = Date.now() - totalStart;
|
|
1496
1562
|
return {
|
|
1497
1563
|
terminationReason: "completed",
|
|
@@ -1500,25 +1566,8 @@ async function* runWarpGrepStreaming(config) {
|
|
|
1500
1566
|
timings
|
|
1501
1567
|
};
|
|
1502
1568
|
}
|
|
1503
|
-
timings.turns.push(turnMetrics);
|
|
1504
1569
|
break;
|
|
1505
1570
|
}
|
|
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);
|
|
1522
1571
|
}
|
|
1523
1572
|
if (terminationReason !== "completed" || !finishMeta) {
|
|
1524
1573
|
timings.total_ms = Date.now() - totalStart;
|
|
@@ -1562,14 +1611,6 @@ async function* runWarpGrepStreaming(config) {
|
|
|
1562
1611
|
timings
|
|
1563
1612
|
};
|
|
1564
1613
|
}
|
|
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
|
-
}
|
|
1573
1614
|
|
|
1574
1615
|
// tools/warp_grep/providers/remote.ts
|
|
1575
1616
|
init_config();
|
|
@@ -1749,35 +1790,6 @@ var RemoteCommandsProvider = class {
|
|
|
1749
1790
|
return [];
|
|
1750
1791
|
}
|
|
1751
1792
|
}
|
|
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
|
-
}
|
|
1781
1793
|
};
|
|
1782
1794
|
|
|
1783
1795
|
// tools/warp_grep/providers/code_storage_http.ts
|
|
@@ -1804,9 +1816,9 @@ function createCodeStorageHttpCommands(config) {
|
|
|
1804
1816
|
const { baseUrl, repoId, branch } = config;
|
|
1805
1817
|
const encodedRepoId = encodeURIComponent(repoId);
|
|
1806
1818
|
return {
|
|
1807
|
-
grep: (pattern,
|
|
1808
|
-
read: (
|
|
1809
|
-
listDir: (
|
|
1819
|
+
grep: (pattern, path5, glob) => post(`${baseUrl}/api/code-search/${encodedRepoId}/grep`, { pattern, path: path5, glob, branch }, "grep"),
|
|
1820
|
+
read: (path5, start, end) => post(`${baseUrl}/api/code-search/${encodedRepoId}/read`, { path: path5, start, end, branch }, "read"),
|
|
1821
|
+
listDir: (path5, maxDepth) => post(`${baseUrl}/api/code-search/${encodedRepoId}/list`, { path: path5, maxDepth, branch }, "list")
|
|
1810
1822
|
};
|
|
1811
1823
|
}
|
|
1812
1824
|
|
|
@@ -2138,25 +2150,6 @@ init_paths();
|
|
|
2138
2150
|
// tools/warp_grep/agent/index.ts
|
|
2139
2151
|
init_config();
|
|
2140
2152
|
|
|
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
|
-
|
|
2160
2153
|
// tools/warp_grep/index.ts
|
|
2161
2154
|
var warpGrepInputSchema = import_zod.z.object({
|
|
2162
2155
|
search_term: import_zod.z.string().describe("Search problem statement that this subagent is supposed to research for")
|