@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
|
});
|
|
@@ -634,58 +593,131 @@ module.exports = __toCommonJS(openai_exports);
|
|
|
634
593
|
init_config();
|
|
635
594
|
|
|
636
595
|
// tools/warp_grep/agent/parser.ts
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
const trimmed = rangeStr.trim();
|
|
641
|
-
if (!trimmed) continue;
|
|
642
|
-
const parts = trimmed.split("-").map((v) => parseInt(v.trim(), 10));
|
|
643
|
-
if (parts.length >= 2 && Number.isFinite(parts[0]) && Number.isFinite(parts[1])) {
|
|
644
|
-
ranges.push([parts[0], parts[1]]);
|
|
645
|
-
} else if (Number.isFinite(parts[0])) {
|
|
646
|
-
ranges.push([parts[0], parts[0]]);
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
if (ranges.length === 1) return { start: ranges[0][0], end: ranges[0][1] };
|
|
650
|
-
if (ranges.length > 1) return { lines: ranges };
|
|
651
|
-
return {};
|
|
596
|
+
var VALID_COMMANDS = ["list_directory", "ripgrep", "read", "finish"];
|
|
597
|
+
function isValidCommand(name) {
|
|
598
|
+
return VALID_COMMANDS.includes(name);
|
|
652
599
|
}
|
|
653
|
-
function
|
|
654
|
-
const
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
const
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
files.push({ path: filePath, lines: "*" });
|
|
667
|
-
continue;
|
|
600
|
+
function parseQwen3ToolCalls(text) {
|
|
601
|
+
const tools = [];
|
|
602
|
+
const toolCallRegex = /<tool_call>\s*<function=([a-z_][a-z0-9_]*)>([\s\S]*?)<\/function>\s*<\/tool_call>/gi;
|
|
603
|
+
let match;
|
|
604
|
+
while ((match = toolCallRegex.exec(text)) !== null) {
|
|
605
|
+
const funcName = match[1].toLowerCase();
|
|
606
|
+
const body = match[2];
|
|
607
|
+
if (!isValidCommand(funcName)) continue;
|
|
608
|
+
const params = {};
|
|
609
|
+
const paramRegex = /<parameter=([a-z_][a-z0-9_]*)>([\s\S]*?)<\/parameter>/gi;
|
|
610
|
+
let paramMatch;
|
|
611
|
+
while ((paramMatch = paramRegex.exec(body)) !== null) {
|
|
612
|
+
params[paramMatch[1].toLowerCase()] = paramMatch[2].trim();
|
|
668
613
|
}
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
614
|
+
if (funcName === "ripgrep") {
|
|
615
|
+
const pattern = params.pattern;
|
|
616
|
+
if (!pattern) continue;
|
|
617
|
+
const args = {
|
|
618
|
+
pattern,
|
|
619
|
+
path: params.path || ".",
|
|
620
|
+
...params.glob && { glob: params.glob },
|
|
621
|
+
...params.context_lines && { context_lines: parseInt(params.context_lines, 10) },
|
|
622
|
+
...params.case_sensitive && { case_sensitive: params.case_sensitive === "true" }
|
|
623
|
+
};
|
|
624
|
+
tools.push({ name: "grep", arguments: args });
|
|
625
|
+
} else if (funcName === "list_directory") {
|
|
626
|
+
const command = params.command;
|
|
627
|
+
const directPath = params.path;
|
|
628
|
+
let dirPath = directPath || ".";
|
|
629
|
+
if (!directPath && command) {
|
|
630
|
+
const tokens = command.trim().split(/\s+/);
|
|
631
|
+
const pathTokens = tokens.slice(1).filter((t) => !t.startsWith("-") && !t.startsWith("|") && !t.startsWith("\\("));
|
|
632
|
+
if (pathTokens.length > 0) {
|
|
633
|
+
dirPath = pathTokens[0];
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
tools.push({ name: "list_directory", arguments: { path: dirPath, pattern: params.pattern || null } });
|
|
637
|
+
} else if (funcName === "read") {
|
|
638
|
+
const filePath = params.path;
|
|
639
|
+
if (!filePath) continue;
|
|
640
|
+
const args = { path: filePath };
|
|
641
|
+
const linesStr = params.lines;
|
|
642
|
+
if (linesStr) {
|
|
643
|
+
const ranges = [];
|
|
644
|
+
for (const rangeStr of linesStr.split(",")) {
|
|
645
|
+
const trimmed = rangeStr.trim();
|
|
646
|
+
if (!trimmed) continue;
|
|
647
|
+
const [s, e] = trimmed.split("-").map((v) => parseInt(v.trim(), 10));
|
|
648
|
+
if (Number.isFinite(s) && Number.isFinite(e)) {
|
|
649
|
+
ranges.push([s, e]);
|
|
650
|
+
} else if (Number.isFinite(s)) {
|
|
651
|
+
ranges.push([s, s]);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
if (ranges.length === 1) {
|
|
655
|
+
args.start = ranges[0][0];
|
|
656
|
+
args.end = ranges[0][1];
|
|
657
|
+
} else if (ranges.length > 1) {
|
|
658
|
+
args.lines = ranges;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
tools.push({ name: "read", arguments: args });
|
|
662
|
+
} else if (funcName === "finish") {
|
|
663
|
+
if (params.result && !params.files) {
|
|
664
|
+
tools.push({ name: "finish", arguments: { files: [], textResult: params.result } });
|
|
665
|
+
continue;
|
|
666
|
+
}
|
|
667
|
+
const filesStr = params.files;
|
|
668
|
+
if (!filesStr) {
|
|
669
|
+
tools.push({ name: "finish", arguments: { files: [], textResult: "No relevant code found." } });
|
|
670
|
+
continue;
|
|
671
|
+
}
|
|
672
|
+
const files = [];
|
|
673
|
+
for (const line of filesStr.split("\n")) {
|
|
674
|
+
const trimmed = line.trim();
|
|
675
|
+
if (!trimmed) continue;
|
|
676
|
+
const colonIdx = trimmed.indexOf(":");
|
|
677
|
+
if (colonIdx === -1) {
|
|
678
|
+
files.push({ path: trimmed, lines: "*" });
|
|
679
|
+
} else {
|
|
680
|
+
const filePath = trimmed.slice(0, colonIdx);
|
|
681
|
+
const rangesPart = trimmed.slice(colonIdx + 1);
|
|
682
|
+
const ranges = [];
|
|
683
|
+
for (const rangeStr of rangesPart.split(",")) {
|
|
684
|
+
const rt = rangeStr.trim();
|
|
685
|
+
if (!rt || rt === "*") {
|
|
686
|
+
files.push({ path: filePath, lines: "*" });
|
|
687
|
+
break;
|
|
688
|
+
}
|
|
689
|
+
const [s, e] = rt.split("-").map((v) => parseInt(v.trim(), 10));
|
|
690
|
+
if (Number.isFinite(s) && Number.isFinite(e)) {
|
|
691
|
+
ranges.push([s, e]);
|
|
692
|
+
} else if (Number.isFinite(s)) {
|
|
693
|
+
ranges.push([s, s]);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
if (ranges.length > 0) {
|
|
697
|
+
files.push({ path: filePath, lines: ranges });
|
|
698
|
+
} else if (!files.some((f) => f.path === filePath)) {
|
|
699
|
+
files.push({ path: filePath, lines: "*" });
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
if (files.length > 0) {
|
|
704
|
+
tools.push({ name: "finish", arguments: { files } });
|
|
705
|
+
} else {
|
|
706
|
+
tools.push({ name: "finish", arguments: { files: [], textResult: filesStr } });
|
|
678
707
|
}
|
|
679
708
|
}
|
|
680
|
-
files.push({ path: filePath, lines: ranges.length > 0 ? ranges : "*" });
|
|
681
709
|
}
|
|
682
|
-
return
|
|
683
|
-
}
|
|
684
|
-
function extractPathFromCommand(command) {
|
|
685
|
-
const tokens = command.trim().split(/\s+/);
|
|
686
|
-
const pathTokens = tokens.slice(1).filter((t) => !t.startsWith("-") && !t.startsWith("|") && !t.startsWith("\\("));
|
|
687
|
-
return pathTokens[0] || ".";
|
|
710
|
+
return tools;
|
|
688
711
|
}
|
|
712
|
+
var LLMResponseParser = class {
|
|
713
|
+
parse(text) {
|
|
714
|
+
if (typeof text !== "string") {
|
|
715
|
+
throw new TypeError("Command text must be a string.");
|
|
716
|
+
}
|
|
717
|
+
const withoutThink = text.replace(/<think>[\s\S]*?<\/think>/gi, "");
|
|
718
|
+
return parseQwen3ToolCalls(withoutThink);
|
|
719
|
+
}
|
|
720
|
+
};
|
|
689
721
|
|
|
690
722
|
// tools/warp_grep/agent/tools/grep.ts
|
|
691
723
|
async function toolGrep(provider, args) {
|
|
@@ -736,42 +768,29 @@ async function toolRead(provider, args) {
|
|
|
736
768
|
|
|
737
769
|
// tools/warp_grep/agent/tools/list_directory.ts
|
|
738
770
|
init_config();
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
const
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
return res.error;
|
|
763
|
-
}
|
|
764
|
-
if (!res.files.length) {
|
|
765
|
-
return "no matches";
|
|
766
|
-
}
|
|
767
|
-
const header = `Found ${res.totalFound} file(s) matching "${args.pattern}" within ${res.searchDir}, sorted by modification time (newest first):`;
|
|
768
|
-
const body = res.files.join("\n");
|
|
769
|
-
const truncated = res.totalFound > res.files.length ? `
|
|
770
|
-
[${res.totalFound - res.files.length} files truncated]` : "";
|
|
771
|
-
return `${header}
|
|
772
|
-
---
|
|
773
|
-
${body}
|
|
774
|
-
---${truncated}`;
|
|
771
|
+
async function toolListDirectory(provider, args) {
|
|
772
|
+
const maxResults = args.maxResults ?? AGENT_CONFIG.MAX_OUTPUT_LINES;
|
|
773
|
+
const initialDepth = args.maxDepth ?? AGENT_CONFIG.MAX_LIST_DEPTH;
|
|
774
|
+
async function getListRecursive(currentDepth) {
|
|
775
|
+
const entries = await provider.listDirectory({
|
|
776
|
+
path: args.path,
|
|
777
|
+
pattern: args.pattern ?? null,
|
|
778
|
+
maxResults,
|
|
779
|
+
maxDepth: currentDepth
|
|
780
|
+
});
|
|
781
|
+
if (entries.length >= maxResults && currentDepth > 0) {
|
|
782
|
+
return getListRecursive(currentDepth - 1);
|
|
783
|
+
}
|
|
784
|
+
return { entries };
|
|
785
|
+
}
|
|
786
|
+
const { entries: list } = await getListRecursive(initialDepth);
|
|
787
|
+
if (!list.length) return "empty";
|
|
788
|
+
const tree = list.map((e) => {
|
|
789
|
+
const indent = " ".repeat(e.depth);
|
|
790
|
+
const name = e.type === "dir" ? `${e.name}/` : e.name;
|
|
791
|
+
return `${indent}${name}`;
|
|
792
|
+
}).join("\n");
|
|
793
|
+
return tree;
|
|
775
794
|
}
|
|
776
795
|
|
|
777
796
|
// tools/warp_grep/agent/tools/finish.ts
|
|
@@ -832,21 +851,32 @@ function mergeRanges(ranges) {
|
|
|
832
851
|
return merged;
|
|
833
852
|
}
|
|
834
853
|
|
|
835
|
-
// tools/warp_grep/agent/
|
|
836
|
-
var
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
if (m.role === "assistant") {
|
|
842
|
-
let size = typeof m.content === "string" ? m.content.length : 0;
|
|
843
|
-
if (m.tool_calls) {
|
|
844
|
-
size += m.tool_calls.reduce((s, tc) => s + tc.function.name.length + tc.function.arguments.length, 0);
|
|
854
|
+
// tools/warp_grep/agent/formatter.ts
|
|
855
|
+
var ToolOutputFormatter = class {
|
|
856
|
+
format(toolName, _args, output, options = {}) {
|
|
857
|
+
const name = (toolName ?? "").trim();
|
|
858
|
+
if (!name) {
|
|
859
|
+
return "";
|
|
845
860
|
}
|
|
846
|
-
|
|
861
|
+
const payload = output?.toString?.()?.trim?.() ?? "";
|
|
862
|
+
const isError = Boolean(options.isError);
|
|
863
|
+
if (!payload && !isError) {
|
|
864
|
+
return "";
|
|
865
|
+
}
|
|
866
|
+
return `<tool_response>
|
|
867
|
+
${payload}
|
|
868
|
+
</tool_response>`;
|
|
847
869
|
}
|
|
848
|
-
|
|
870
|
+
};
|
|
871
|
+
var sharedFormatter = new ToolOutputFormatter();
|
|
872
|
+
function formatAgentToolOutput(toolName, args, output, options = {}) {
|
|
873
|
+
return sharedFormatter.format(toolName, args, output, options);
|
|
849
874
|
}
|
|
875
|
+
|
|
876
|
+
// tools/warp_grep/agent/helpers.ts
|
|
877
|
+
var import_path = __toESM(require("path"), 1);
|
|
878
|
+
init_config();
|
|
879
|
+
var TRUNCATED_MARKER = "[truncated for context limit]";
|
|
850
880
|
function formatTurnMessage(turnsUsed, maxTurns) {
|
|
851
881
|
const turnsRemaining = maxTurns - turnsUsed;
|
|
852
882
|
if (turnsRemaining === 1) {
|
|
@@ -857,7 +887,7 @@ You have used ${turnsUsed} turns, you only have 1 turn remaining. You have run o
|
|
|
857
887
|
You have used ${turnsUsed} turn${turnsUsed === 1 ? "" : "s"} and have ${turnsRemaining} remaining`;
|
|
858
888
|
}
|
|
859
889
|
function calculateContextBudget(messages) {
|
|
860
|
-
const totalChars = messages.reduce((sum, m) => sum +
|
|
890
|
+
const totalChars = messages.reduce((sum, m) => sum + m.content.length, 0);
|
|
861
891
|
const maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS;
|
|
862
892
|
const percent = Math.round(totalChars / maxChars * 100);
|
|
863
893
|
const usedK = Math.round(totalChars / 1e3);
|
|
@@ -866,21 +896,24 @@ function calculateContextBudget(messages) {
|
|
|
866
896
|
}
|
|
867
897
|
async function buildInitialState(repoRoot, searchTerm, provider, options) {
|
|
868
898
|
const budget = calculateContextBudget([]);
|
|
869
|
-
const turnTag = `
|
|
899
|
+
const turnTag = `Turn 0/${AGENT_CONFIG.MAX_TURNS}`;
|
|
870
900
|
const treeDepth = options?.search_type === "node_modules" ? 1 : 2;
|
|
871
|
-
const absRoot = import_path2.default.resolve(repoRoot);
|
|
872
901
|
try {
|
|
873
902
|
const entries = await provider.listDirectory({
|
|
874
903
|
path: ".",
|
|
875
904
|
maxResults: AGENT_CONFIG.MAX_OUTPUT_LINES,
|
|
876
905
|
maxDepth: treeDepth
|
|
877
906
|
});
|
|
878
|
-
const
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
907
|
+
const treeLines = entries.map((e) => {
|
|
908
|
+
const indent = " ".repeat(e.depth);
|
|
909
|
+
const name = e.type === "dir" ? `${e.name}/` : e.name;
|
|
910
|
+
return `${indent}${name}`;
|
|
911
|
+
});
|
|
912
|
+
const repoName = import_path.default.basename(repoRoot);
|
|
913
|
+
const treeOutput = treeLines.length > 0 ? `${repoName}/
|
|
914
|
+
${treeLines.join("\n")}` : `${repoName}/`;
|
|
882
915
|
return `<repo_structure>
|
|
883
|
-
${
|
|
916
|
+
${treeOutput}
|
|
884
917
|
</repo_structure>
|
|
885
918
|
|
|
886
919
|
<search_string>
|
|
@@ -889,8 +922,9 @@ ${searchTerm}
|
|
|
889
922
|
${budget}
|
|
890
923
|
${turnTag}`;
|
|
891
924
|
} catch {
|
|
925
|
+
const repoName = import_path.default.basename(repoRoot);
|
|
892
926
|
return `<repo_structure>
|
|
893
|
-
${
|
|
927
|
+
${repoName}/
|
|
894
928
|
</repo_structure>
|
|
895
929
|
|
|
896
930
|
<search_string>
|
|
@@ -901,32 +935,26 @@ ${turnTag}`;
|
|
|
901
935
|
}
|
|
902
936
|
}
|
|
903
937
|
function enforceContextLimit(messages, maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS) {
|
|
904
|
-
const getTotalChars = () => messages.reduce((sum, m) => sum +
|
|
938
|
+
const getTotalChars = () => messages.reduce((sum, m) => sum + m.content.length, 0);
|
|
905
939
|
if (getTotalChars() <= maxChars) {
|
|
906
940
|
return messages;
|
|
907
941
|
}
|
|
908
|
-
const
|
|
942
|
+
const userIndices = [];
|
|
909
943
|
let firstUserSkipped = false;
|
|
910
944
|
for (let i = 0; i < messages.length; i++) {
|
|
911
|
-
|
|
912
|
-
if (m.role === "tool") {
|
|
913
|
-
truncatableIndices.push(i);
|
|
914
|
-
} else if (m.role === "user") {
|
|
945
|
+
if (messages[i].role === "user") {
|
|
915
946
|
if (!firstUserSkipped) {
|
|
916
947
|
firstUserSkipped = true;
|
|
917
948
|
continue;
|
|
918
949
|
}
|
|
919
|
-
|
|
950
|
+
userIndices.push(i);
|
|
920
951
|
}
|
|
921
952
|
}
|
|
922
|
-
for (const idx of
|
|
953
|
+
for (const idx of userIndices) {
|
|
923
954
|
if (getTotalChars() <= maxChars) {
|
|
924
955
|
break;
|
|
925
956
|
}
|
|
926
|
-
|
|
927
|
-
if (m.role === "tool" && m.content !== TRUNCATED_MARKER) {
|
|
928
|
-
messages[idx] = { role: "tool", tool_call_id: m.tool_call_id, content: TRUNCATED_MARKER };
|
|
929
|
-
} else if (m.role === "user" && m.content !== TRUNCATED_MARKER) {
|
|
957
|
+
if (messages[idx].content !== TRUNCATED_MARKER) {
|
|
930
958
|
messages[idx] = { role: "user", content: TRUNCATED_MARKER };
|
|
931
959
|
}
|
|
932
960
|
}
|
|
@@ -939,7 +967,7 @@ var import_openai = __toESM(require("openai"), 1);
|
|
|
939
967
|
// package.json
|
|
940
968
|
var package_default = {
|
|
941
969
|
name: "@morphllm/morphsdk",
|
|
942
|
-
version: "0.2.
|
|
970
|
+
version: "0.2.146",
|
|
943
971
|
description: "TypeScript SDK and CLI for Morph Fast Apply integration",
|
|
944
972
|
type: "module",
|
|
945
973
|
main: "./dist/index.cjs",
|
|
@@ -1178,115 +1206,9 @@ var package_default = {
|
|
|
1178
1206
|
var SDK_VERSION = package_default.version;
|
|
1179
1207
|
|
|
1180
1208
|
// tools/warp_grep/agent/runner.ts
|
|
1181
|
-
var
|
|
1209
|
+
var import_path2 = __toESM(require("path"), 1);
|
|
1210
|
+
var parser = new LLMResponseParser();
|
|
1182
1211
|
var DEFAULT_API_URL = "https://api.morphllm.com";
|
|
1183
|
-
var TOOL_SPECS = [
|
|
1184
|
-
{
|
|
1185
|
-
type: "function",
|
|
1186
|
-
function: {
|
|
1187
|
-
name: "list_directory",
|
|
1188
|
-
description: "Execute ls or find commands to explore directory structure. Max 500 results. Common junk directories are excluded automatically.",
|
|
1189
|
-
parameters: {
|
|
1190
|
-
type: "object",
|
|
1191
|
-
properties: {
|
|
1192
|
-
command: {
|
|
1193
|
-
type: "string",
|
|
1194
|
-
description: "Full ls or find command (e.g. ls -la src/, find . -maxdepth 2 -type f -name '*.py', find . -type d, ls -d */)."
|
|
1195
|
-
}
|
|
1196
|
-
},
|
|
1197
|
-
required: ["command"]
|
|
1198
|
-
}
|
|
1199
|
-
}
|
|
1200
|
-
},
|
|
1201
|
-
{
|
|
1202
|
-
type: "function",
|
|
1203
|
-
function: {
|
|
1204
|
-
name: "grep_search",
|
|
1205
|
-
description: "Search for a regex pattern in file contents. Returns matching lines with file paths and line numbers. Case-insensitive. Respects .gitignore.",
|
|
1206
|
-
parameters: {
|
|
1207
|
-
type: "object",
|
|
1208
|
-
properties: {
|
|
1209
|
-
pattern: {
|
|
1210
|
-
type: "string",
|
|
1211
|
-
description: "Regex pattern to search for in file contents (e.g. 'class\\s+\\w+Error', 'import|require|from', 'def (get|set|update)_user')."
|
|
1212
|
-
},
|
|
1213
|
-
path: {
|
|
1214
|
-
type: "string",
|
|
1215
|
-
description: "File or directory to search in. Defaults to current working directory."
|
|
1216
|
-
},
|
|
1217
|
-
glob: {
|
|
1218
|
-
type: "string",
|
|
1219
|
-
description: "Glob pattern to filter files (e.g. '*.py', '*.{ts,tsx,js,jsx,py,go}', 'src/**/*.go', '!*.test.*')."
|
|
1220
|
-
},
|
|
1221
|
-
limit: {
|
|
1222
|
-
type: "integer",
|
|
1223
|
-
description: "Limit output to first N matching lines. Shows all matches if not specified."
|
|
1224
|
-
}
|
|
1225
|
-
},
|
|
1226
|
-
required: ["pattern"]
|
|
1227
|
-
}
|
|
1228
|
-
}
|
|
1229
|
-
},
|
|
1230
|
-
{
|
|
1231
|
-
type: "function",
|
|
1232
|
-
function: {
|
|
1233
|
-
name: "glob",
|
|
1234
|
-
description: "Find files by name/extension using glob patterns. Returns absolute paths sorted by modification time (newest first). Respects .gitignore. Max 100 results.",
|
|
1235
|
-
parameters: {
|
|
1236
|
-
type: "object",
|
|
1237
|
-
properties: {
|
|
1238
|
-
pattern: {
|
|
1239
|
-
type: "string",
|
|
1240
|
-
description: "Glob pattern to match files (e.g. '*.py', 'src/**/*.js', '*.{ts,tsx}', 'test_*.py')."
|
|
1241
|
-
},
|
|
1242
|
-
path: {
|
|
1243
|
-
type: "string",
|
|
1244
|
-
description: "Directory to search in. Defaults to repository root."
|
|
1245
|
-
}
|
|
1246
|
-
},
|
|
1247
|
-
required: ["pattern"]
|
|
1248
|
-
}
|
|
1249
|
-
}
|
|
1250
|
-
},
|
|
1251
|
-
{
|
|
1252
|
-
type: "function",
|
|
1253
|
-
function: {
|
|
1254
|
-
name: "read",
|
|
1255
|
-
description: "Read entire files or specific line ranges using absolute paths.",
|
|
1256
|
-
parameters: {
|
|
1257
|
-
type: "object",
|
|
1258
|
-
properties: {
|
|
1259
|
-
path: {
|
|
1260
|
-
type: "string",
|
|
1261
|
-
description: "File path to read, using absolute path (e.g. '/home/ubuntu/repo/src/main.py' or windows path)."
|
|
1262
|
-
},
|
|
1263
|
-
lines: {
|
|
1264
|
-
type: "string",
|
|
1265
|
-
description: "Optional line range (e.g. '1-50' or '1-20,45-80'). Omit to read entire file."
|
|
1266
|
-
}
|
|
1267
|
-
},
|
|
1268
|
-
required: ["path"]
|
|
1269
|
-
}
|
|
1270
|
-
}
|
|
1271
|
-
},
|
|
1272
|
-
{
|
|
1273
|
-
type: "function",
|
|
1274
|
-
function: {
|
|
1275
|
-
name: "finish",
|
|
1276
|
-
description: "Submit final answer with all relevant code locations. Include imports and over-include rather than miss context.",
|
|
1277
|
-
parameters: {
|
|
1278
|
-
type: "object",
|
|
1279
|
-
properties: {
|
|
1280
|
-
files: {
|
|
1281
|
-
type: "string",
|
|
1282
|
-
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."
|
|
1283
|
-
}
|
|
1284
|
-
},
|
|
1285
|
-
required: ["files"]
|
|
1286
|
-
}
|
|
1287
|
-
}
|
|
1288
|
-
}
|
|
1289
|
-
];
|
|
1290
1212
|
async function callModel(messages, model, options = {}) {
|
|
1291
1213
|
const baseUrl = options.morphApiUrl || DEFAULT_API_URL;
|
|
1292
1214
|
const apiKey = options.morphApiKey || process.env.MORPH_API_KEY || "";
|
|
@@ -1307,9 +1229,8 @@ async function callModel(messages, model, options = {}) {
|
|
|
1307
1229
|
data = await client.chat.completions.create({
|
|
1308
1230
|
model,
|
|
1309
1231
|
temperature: 0,
|
|
1310
|
-
max_tokens:
|
|
1232
|
+
max_tokens: 1024,
|
|
1311
1233
|
messages,
|
|
1312
|
-
tools: TOOL_SPECS,
|
|
1313
1234
|
...options.search_type ? { search_type: options.search_type } : {}
|
|
1314
1235
|
});
|
|
1315
1236
|
} catch (error) {
|
|
@@ -1321,87 +1242,187 @@ async function callModel(messages, model, options = {}) {
|
|
|
1321
1242
|
throw error;
|
|
1322
1243
|
}
|
|
1323
1244
|
const choice = data?.choices?.[0];
|
|
1324
|
-
const
|
|
1325
|
-
if (
|
|
1326
|
-
|
|
1327
|
-
throw new Error("Invalid response from model: no message in response");
|
|
1328
|
-
}
|
|
1329
|
-
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
1330
|
-
continue;
|
|
1331
|
-
}
|
|
1332
|
-
const toolCalls = (message.tool_calls || []).map((tc) => ({
|
|
1333
|
-
id: tc.id,
|
|
1334
|
-
type: "function",
|
|
1335
|
-
function: { name: tc.function.name, arguments: tc.function.arguments }
|
|
1336
|
-
}));
|
|
1337
|
-
if (message.content || toolCalls.length > 0) {
|
|
1338
|
-
return { content: message.content ?? null, tool_calls: toolCalls };
|
|
1245
|
+
const content = choice?.message?.content;
|
|
1246
|
+
if (content && typeof content === "string") {
|
|
1247
|
+
return content;
|
|
1339
1248
|
}
|
|
1340
1249
|
if (attempt === MAX_EMPTY_RETRIES) {
|
|
1341
1250
|
const finishReason = choice?.finish_reason ?? "unknown";
|
|
1251
|
+
const hasToolCalls = Array.isArray(choice?.message?.tool_calls) && choice.message.tool_calls.length > 0;
|
|
1252
|
+
const choicesLen = data?.choices?.length ?? 0;
|
|
1253
|
+
const contentType = content === null ? "null" : content === void 0 ? "undefined" : typeof content;
|
|
1342
1254
|
throw new Error(
|
|
1343
|
-
`Invalid response from model:
|
|
1255
|
+
`Invalid response from model: content=${contentType}, finish_reason=${finishReason}, has_tool_calls=${hasToolCalls}, choices_length=${choicesLen}`
|
|
1344
1256
|
);
|
|
1345
1257
|
}
|
|
1346
1258
|
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
1347
1259
|
}
|
|
1348
1260
|
throw new Error("Invalid response from model");
|
|
1349
1261
|
}
|
|
1350
|
-
function
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1262
|
+
async function runWarpGrep(config) {
|
|
1263
|
+
const totalStart = Date.now();
|
|
1264
|
+
const timeoutMs = config.timeout ?? AGENT_CONFIG.TIMEOUT_MS;
|
|
1265
|
+
const timings = { turns: [], timeout_ms: timeoutMs };
|
|
1266
|
+
const repoRoot = import_path2.default.resolve(config.repoRoot || process.cwd());
|
|
1267
|
+
const model = config.model || DEFAULT_MODEL;
|
|
1268
|
+
const messages = [];
|
|
1269
|
+
const maxTurns = AGENT_CONFIG.MAX_TURNS;
|
|
1270
|
+
const initialStateStart = Date.now();
|
|
1271
|
+
const initialState = await buildInitialState(repoRoot, config.searchTerm, config.provider, { search_type: config.search_type });
|
|
1272
|
+
timings.initial_state_ms = Date.now() - initialStateStart;
|
|
1273
|
+
messages.push({ role: "user", content: initialState });
|
|
1274
|
+
const provider = config.provider;
|
|
1275
|
+
const errors = [];
|
|
1276
|
+
let finishMeta;
|
|
1277
|
+
let terminationReason = "terminated";
|
|
1278
|
+
for (let turn = 1; turn <= maxTurns; turn += 1) {
|
|
1279
|
+
const turnMetrics = { turn, morph_api_ms: 0, local_tools_ms: 0 };
|
|
1280
|
+
enforceContextLimit(messages);
|
|
1281
|
+
const modelCallStart = Date.now();
|
|
1282
|
+
const assistantContent = await callModel(messages, model, {
|
|
1283
|
+
morphApiKey: config.morphApiKey,
|
|
1284
|
+
morphApiUrl: config.morphApiUrl,
|
|
1285
|
+
retryConfig: config.retryConfig,
|
|
1286
|
+
timeout: timeoutMs,
|
|
1287
|
+
search_type: config.search_type
|
|
1288
|
+
}).catch((e) => {
|
|
1289
|
+
const errMsg = e instanceof Error ? e.message : String(e);
|
|
1290
|
+
console.error(`[warp_grep] Morph API call failed on turn ${turn}:`, errMsg);
|
|
1291
|
+
errors.push({ message: errMsg });
|
|
1292
|
+
return "";
|
|
1293
|
+
});
|
|
1294
|
+
turnMetrics.morph_api_ms = Date.now() - modelCallStart;
|
|
1295
|
+
if (!assistantContent) {
|
|
1296
|
+
console.error(`[warp_grep] Empty response from Morph API on turn ${turn}. Errors so far:`, errors);
|
|
1297
|
+
timings.turns.push(turnMetrics);
|
|
1298
|
+
break;
|
|
1376
1299
|
}
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
});
|
|
1300
|
+
messages.push({ role: "assistant", content: assistantContent });
|
|
1301
|
+
const toolCalls = parser.parse(assistantContent);
|
|
1302
|
+
if (toolCalls.length === 0) {
|
|
1303
|
+
console.error(`[warp_grep] No tool calls parsed on turn ${turn}. Assistant content (first 500 chars):`, assistantContent.slice(0, 500));
|
|
1304
|
+
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" });
|
|
1305
|
+
terminationReason = "terminated";
|
|
1306
|
+
timings.turns.push(turnMetrics);
|
|
1307
|
+
break;
|
|
1382
1308
|
}
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1309
|
+
const finishCalls = toolCalls.filter((c) => c.name === "finish");
|
|
1310
|
+
const grepCalls = toolCalls.filter((c) => c.name === "grep");
|
|
1311
|
+
const listDirCalls = toolCalls.filter((c) => c.name === "list_directory");
|
|
1312
|
+
const readCalls = toolCalls.filter((c) => c.name === "read");
|
|
1313
|
+
const skipCalls = toolCalls.filter((c) => c.name === "_skip");
|
|
1314
|
+
const formatted = [];
|
|
1315
|
+
for (const c of skipCalls) {
|
|
1316
|
+
const msg = c.arguments?.message || "Command skipped due to parsing error";
|
|
1317
|
+
formatted.push(msg);
|
|
1386
1318
|
}
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1319
|
+
const allPromises = [];
|
|
1320
|
+
for (const c of grepCalls) {
|
|
1321
|
+
const args = c.arguments ?? {};
|
|
1322
|
+
allPromises.push(
|
|
1323
|
+
toolGrep(provider, args).then(
|
|
1324
|
+
({ output }) => formatAgentToolOutput("grep", args, output),
|
|
1325
|
+
(err) => formatAgentToolOutput("grep", args, String(err), { isError: true })
|
|
1326
|
+
)
|
|
1327
|
+
);
|
|
1328
|
+
}
|
|
1329
|
+
for (const c of listDirCalls) {
|
|
1330
|
+
const args = c.arguments ?? {};
|
|
1331
|
+
allPromises.push(
|
|
1332
|
+
toolListDirectory(provider, args).then(
|
|
1333
|
+
(p) => formatAgentToolOutput("list_directory", args, p),
|
|
1334
|
+
(err) => formatAgentToolOutput("list_directory", args, String(err), { isError: true })
|
|
1335
|
+
)
|
|
1336
|
+
);
|
|
1337
|
+
}
|
|
1338
|
+
for (const c of readCalls) {
|
|
1339
|
+
const args = c.arguments ?? {};
|
|
1340
|
+
allPromises.push(
|
|
1341
|
+
toolRead(provider, args).then(
|
|
1342
|
+
(p) => formatAgentToolOutput("read", args, p),
|
|
1343
|
+
(err) => formatAgentToolOutput("read", args, String(err), { isError: true })
|
|
1344
|
+
)
|
|
1345
|
+
);
|
|
1346
|
+
}
|
|
1347
|
+
const toolExecStart = Date.now();
|
|
1348
|
+
const allResults = await Promise.all(allPromises);
|
|
1349
|
+
turnMetrics.local_tools_ms = Date.now() - toolExecStart;
|
|
1350
|
+
for (const result of allResults) {
|
|
1351
|
+
formatted.push(result);
|
|
1352
|
+
}
|
|
1353
|
+
if (formatted.length > 0) {
|
|
1354
|
+
const turnMessage = formatTurnMessage(turn, maxTurns);
|
|
1355
|
+
const contextBudget = calculateContextBudget(messages);
|
|
1356
|
+
messages.push({ role: "user", content: formatted.join("\n") + turnMessage + "\n" + contextBudget });
|
|
1357
|
+
}
|
|
1358
|
+
timings.turns.push(turnMetrics);
|
|
1359
|
+
if (finishCalls.length) {
|
|
1360
|
+
const fc = finishCalls[0];
|
|
1361
|
+
const files = fc.arguments?.files ?? [];
|
|
1362
|
+
const textResult = fc.arguments?.textResult;
|
|
1363
|
+
finishMeta = { files };
|
|
1364
|
+
terminationReason = "completed";
|
|
1365
|
+
if (files.length === 0) {
|
|
1366
|
+
const payload2 = textResult || "No relevant code found.";
|
|
1367
|
+
timings.turns.push(turnMetrics);
|
|
1368
|
+
timings.total_ms = Date.now() - totalStart;
|
|
1369
|
+
return {
|
|
1370
|
+
terminationReason: "completed",
|
|
1371
|
+
messages,
|
|
1372
|
+
finish: { payload: payload2, metadata: finishMeta },
|
|
1373
|
+
timings
|
|
1374
|
+
};
|
|
1393
1375
|
}
|
|
1394
|
-
|
|
1376
|
+
break;
|
|
1395
1377
|
}
|
|
1396
|
-
default:
|
|
1397
|
-
return `Unknown tool: ${name}`;
|
|
1398
1378
|
}
|
|
1379
|
+
if (terminationReason !== "completed" || !finishMeta) {
|
|
1380
|
+
timings.total_ms = Date.now() - totalStart;
|
|
1381
|
+
return { terminationReason, messages, errors, timings };
|
|
1382
|
+
}
|
|
1383
|
+
const parts = ["Relevant context found:"];
|
|
1384
|
+
for (const f of finishMeta.files) {
|
|
1385
|
+
const ranges = f.lines === "*" ? "*" : Array.isArray(f.lines) ? f.lines.map(([s, e]) => `${s}-${e}`).join(", ") : "*";
|
|
1386
|
+
parts.push(`- ${f.path}: ${ranges}`);
|
|
1387
|
+
}
|
|
1388
|
+
const payload = parts.join("\n");
|
|
1389
|
+
const finishResolutionStart = Date.now();
|
|
1390
|
+
const fileReadErrors = [];
|
|
1391
|
+
const resolved = await readFinishFiles(
|
|
1392
|
+
repoRoot,
|
|
1393
|
+
finishMeta.files,
|
|
1394
|
+
async (p, s, e) => {
|
|
1395
|
+
try {
|
|
1396
|
+
const rr = await provider.read({ path: p, start: s, end: e });
|
|
1397
|
+
return rr.lines.map((l) => {
|
|
1398
|
+
const idx = l.indexOf("|");
|
|
1399
|
+
return idx >= 0 ? l.slice(idx + 1) : l;
|
|
1400
|
+
});
|
|
1401
|
+
} catch (err) {
|
|
1402
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
1403
|
+
fileReadErrors.push({ path: p, error: errorMsg });
|
|
1404
|
+
console.error(`[warp_grep] Failed to read file: ${p} - ${errorMsg}`);
|
|
1405
|
+
return [`[couldn't find: ${p}]`];
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
);
|
|
1409
|
+
timings.finish_resolution_ms = Date.now() - finishResolutionStart;
|
|
1410
|
+
if (fileReadErrors.length > 0) {
|
|
1411
|
+
errors.push(...fileReadErrors.map((e) => ({ message: `File read error: ${e.path} - ${e.error}` })));
|
|
1412
|
+
}
|
|
1413
|
+
timings.total_ms = Date.now() - totalStart;
|
|
1414
|
+
return {
|
|
1415
|
+
terminationReason: "completed",
|
|
1416
|
+
messages,
|
|
1417
|
+
finish: { payload, metadata: finishMeta, resolved },
|
|
1418
|
+
timings
|
|
1419
|
+
};
|
|
1399
1420
|
}
|
|
1400
1421
|
async function* runWarpGrepStreaming(config) {
|
|
1401
1422
|
const totalStart = Date.now();
|
|
1402
1423
|
const timeoutMs = config.timeout ?? AGENT_CONFIG.TIMEOUT_MS;
|
|
1403
1424
|
const timings = { turns: [], timeout_ms: timeoutMs };
|
|
1404
|
-
const repoRoot =
|
|
1425
|
+
const repoRoot = import_path2.default.resolve(config.repoRoot || process.cwd());
|
|
1405
1426
|
const model = config.model || DEFAULT_MODEL;
|
|
1406
1427
|
const messages = [];
|
|
1407
1428
|
const maxTurns = AGENT_CONFIG.MAX_TURNS;
|
|
@@ -1417,7 +1438,7 @@ async function* runWarpGrepStreaming(config) {
|
|
|
1417
1438
|
const turnMetrics = { turn, morph_api_ms: 0, local_tools_ms: 0 };
|
|
1418
1439
|
enforceContextLimit(messages);
|
|
1419
1440
|
const modelCallStart = Date.now();
|
|
1420
|
-
const
|
|
1441
|
+
const assistantContent = await callModel(messages, model, {
|
|
1421
1442
|
morphApiKey: config.morphApiKey,
|
|
1422
1443
|
morphApiUrl: config.morphApiUrl,
|
|
1423
1444
|
retryConfig: config.retryConfig,
|
|
@@ -1425,45 +1446,90 @@ async function* runWarpGrepStreaming(config) {
|
|
|
1425
1446
|
search_type: config.search_type
|
|
1426
1447
|
}).catch((e) => {
|
|
1427
1448
|
const errMsg = e instanceof Error ? e.message : String(e);
|
|
1428
|
-
console.error(`[warp_grep] Morph API call failed on turn ${turn}:`, errMsg);
|
|
1449
|
+
console.error(`[warp_grep:stream] Morph API call failed on turn ${turn}:`, errMsg);
|
|
1429
1450
|
errors.push({ message: errMsg });
|
|
1430
|
-
return
|
|
1451
|
+
return "";
|
|
1431
1452
|
});
|
|
1432
1453
|
turnMetrics.morph_api_ms = Date.now() - modelCallStart;
|
|
1433
|
-
if (!
|
|
1454
|
+
if (!assistantContent) {
|
|
1455
|
+
console.error(`[warp_grep:stream] Empty response from Morph API on turn ${turn}. Errors so far:`, errors);
|
|
1434
1456
|
timings.turns.push(turnMetrics);
|
|
1435
1457
|
break;
|
|
1436
1458
|
}
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
role: "assistant",
|
|
1440
|
-
content: response.content,
|
|
1441
|
-
...toolCalls.length > 0 ? { tool_calls: toolCalls } : {}
|
|
1442
|
-
});
|
|
1459
|
+
messages.push({ role: "assistant", content: assistantContent });
|
|
1460
|
+
const toolCalls = parser.parse(assistantContent);
|
|
1443
1461
|
if (toolCalls.length === 0) {
|
|
1444
|
-
console.error(`[warp_grep] No tool calls on turn ${turn}.
|
|
1445
|
-
errors.push({ message: "No tool calls produced by the model." });
|
|
1462
|
+
console.error(`[warp_grep:stream] No tool calls parsed on turn ${turn}. Assistant content (first 500 chars):`, assistantContent.slice(0, 500));
|
|
1463
|
+
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" });
|
|
1446
1464
|
terminationReason = "terminated";
|
|
1447
1465
|
timings.turns.push(turnMetrics);
|
|
1448
1466
|
break;
|
|
1449
1467
|
}
|
|
1450
1468
|
yield {
|
|
1451
1469
|
turn,
|
|
1452
|
-
toolCalls: toolCalls.map((
|
|
1453
|
-
name:
|
|
1454
|
-
arguments:
|
|
1470
|
+
toolCalls: toolCalls.map((c) => ({
|
|
1471
|
+
name: c.name,
|
|
1472
|
+
arguments: c.arguments ?? {}
|
|
1455
1473
|
}))
|
|
1456
1474
|
};
|
|
1457
|
-
const
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1475
|
+
const finishCalls = toolCalls.filter((c) => c.name === "finish");
|
|
1476
|
+
const grepCalls = toolCalls.filter((c) => c.name === "grep");
|
|
1477
|
+
const listDirCalls = toolCalls.filter((c) => c.name === "list_directory");
|
|
1478
|
+
const readCalls = toolCalls.filter((c) => c.name === "read");
|
|
1479
|
+
const skipCalls = toolCalls.filter((c) => c.name === "_skip");
|
|
1480
|
+
const formatted = [];
|
|
1481
|
+
for (const c of skipCalls) {
|
|
1482
|
+
const msg = c.arguments?.message || "Command skipped due to parsing error";
|
|
1483
|
+
formatted.push(msg);
|
|
1484
|
+
}
|
|
1485
|
+
const allPromises = [];
|
|
1486
|
+
for (const c of grepCalls) {
|
|
1487
|
+
const args = c.arguments ?? {};
|
|
1488
|
+
allPromises.push(
|
|
1489
|
+
toolGrep(provider, args).then(
|
|
1490
|
+
({ output }) => formatAgentToolOutput("grep", args, output),
|
|
1491
|
+
(err) => formatAgentToolOutput("grep", args, String(err), { isError: true })
|
|
1492
|
+
)
|
|
1493
|
+
);
|
|
1494
|
+
}
|
|
1495
|
+
for (const c of listDirCalls) {
|
|
1496
|
+
const args = c.arguments ?? {};
|
|
1497
|
+
allPromises.push(
|
|
1498
|
+
toolListDirectory(provider, args).then(
|
|
1499
|
+
(p) => formatAgentToolOutput("list_directory", args, p),
|
|
1500
|
+
(err) => formatAgentToolOutput("list_directory", args, String(err), { isError: true })
|
|
1501
|
+
)
|
|
1502
|
+
);
|
|
1503
|
+
}
|
|
1504
|
+
for (const c of readCalls) {
|
|
1505
|
+
const args = c.arguments ?? {};
|
|
1506
|
+
allPromises.push(
|
|
1507
|
+
toolRead(provider, args).then(
|
|
1508
|
+
(p) => formatAgentToolOutput("read", args, p),
|
|
1509
|
+
(err) => formatAgentToolOutput("read", args, String(err), { isError: true })
|
|
1510
|
+
)
|
|
1511
|
+
);
|
|
1512
|
+
}
|
|
1513
|
+
const toolExecStart = Date.now();
|
|
1514
|
+
const allResults = await Promise.all(allPromises);
|
|
1515
|
+
turnMetrics.local_tools_ms = Date.now() - toolExecStart;
|
|
1516
|
+
for (const result of allResults) {
|
|
1517
|
+
formatted.push(result);
|
|
1518
|
+
}
|
|
1519
|
+
if (formatted.length > 0) {
|
|
1520
|
+
const turnMessage = formatTurnMessage(turn, maxTurns);
|
|
1521
|
+
const contextBudget = calculateContextBudget(messages);
|
|
1522
|
+
messages.push({ role: "user", content: formatted.join("\n") + turnMessage + "\n" + contextBudget });
|
|
1523
|
+
}
|
|
1524
|
+
timings.turns.push(turnMetrics);
|
|
1525
|
+
if (finishCalls.length) {
|
|
1526
|
+
const fc = finishCalls[0];
|
|
1527
|
+
const files = fc.arguments?.files ?? [];
|
|
1528
|
+
const textResult = fc.arguments?.textResult;
|
|
1462
1529
|
finishMeta = { files };
|
|
1463
1530
|
terminationReason = "completed";
|
|
1464
1531
|
if (files.length === 0) {
|
|
1465
|
-
const payload2 =
|
|
1466
|
-
timings.turns.push(turnMetrics);
|
|
1532
|
+
const payload2 = textResult || "No relevant code found.";
|
|
1467
1533
|
timings.total_ms = Date.now() - totalStart;
|
|
1468
1534
|
return {
|
|
1469
1535
|
terminationReason: "completed",
|
|
@@ -1472,25 +1538,8 @@ async function* runWarpGrepStreaming(config) {
|
|
|
1472
1538
|
timings
|
|
1473
1539
|
};
|
|
1474
1540
|
}
|
|
1475
|
-
timings.turns.push(turnMetrics);
|
|
1476
1541
|
break;
|
|
1477
1542
|
}
|
|
1478
|
-
const toolExecStart = Date.now();
|
|
1479
|
-
const results = await Promise.all(
|
|
1480
|
-
toolCalls.map(async (tc) => {
|
|
1481
|
-
const args = safeParseJSON(tc.function.arguments);
|
|
1482
|
-
const output = await executeTool(provider, tc.function.name, args, repoRoot).catch((err) => String(err));
|
|
1483
|
-
return { tool_call_id: tc.id, content: output };
|
|
1484
|
-
})
|
|
1485
|
-
);
|
|
1486
|
-
turnMetrics.local_tools_ms = Date.now() - toolExecStart;
|
|
1487
|
-
for (const result of results) {
|
|
1488
|
-
messages.push({ role: "tool", tool_call_id: result.tool_call_id, content: result.content });
|
|
1489
|
-
}
|
|
1490
|
-
const turnMsg = formatTurnMessage(turn, maxTurns);
|
|
1491
|
-
const budget = calculateContextBudget(messages);
|
|
1492
|
-
messages.push({ role: "user", content: turnMsg + "\n" + budget });
|
|
1493
|
-
timings.turns.push(turnMetrics);
|
|
1494
1543
|
}
|
|
1495
1544
|
if (terminationReason !== "completed" || !finishMeta) {
|
|
1496
1545
|
timings.total_ms = Date.now() - totalStart;
|
|
@@ -1534,14 +1583,6 @@ async function* runWarpGrepStreaming(config) {
|
|
|
1534
1583
|
timings
|
|
1535
1584
|
};
|
|
1536
1585
|
}
|
|
1537
|
-
async function runWarpGrep(config) {
|
|
1538
|
-
const gen = runWarpGrepStreaming(config);
|
|
1539
|
-
let result = await gen.next();
|
|
1540
|
-
while (!result.done) {
|
|
1541
|
-
result = await gen.next();
|
|
1542
|
-
}
|
|
1543
|
-
return result.value;
|
|
1544
|
-
}
|
|
1545
1586
|
|
|
1546
1587
|
// tools/warp_grep/providers/remote.ts
|
|
1547
1588
|
init_config();
|
|
@@ -1721,35 +1762,6 @@ var RemoteCommandsProvider = class {
|
|
|
1721
1762
|
return [];
|
|
1722
1763
|
}
|
|
1723
1764
|
}
|
|
1724
|
-
/**
|
|
1725
|
-
* Glob search - finds files matching a pattern.
|
|
1726
|
-
* Falls back to a grep --files approach via the listDir command.
|
|
1727
|
-
*/
|
|
1728
|
-
async glob(params) {
|
|
1729
|
-
const searchPath = params.path || this.repoRoot;
|
|
1730
|
-
try {
|
|
1731
|
-
const stdout = await this.commands.listDir(searchPath, 10);
|
|
1732
|
-
const allPaths = (stdout || "").trim().split(/\r?\n/).filter((p) => p.length > 0);
|
|
1733
|
-
const globToRegex = (glob) => {
|
|
1734
|
-
const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
1735
|
-
return new RegExp(escaped);
|
|
1736
|
-
};
|
|
1737
|
-
const regex = globToRegex(params.pattern);
|
|
1738
|
-
const matched = allPaths.filter((p) => {
|
|
1739
|
-
const name = p.split("/").pop() || "";
|
|
1740
|
-
return regex.test(name) && !shouldSkip(name);
|
|
1741
|
-
});
|
|
1742
|
-
const totalFound = matched.length;
|
|
1743
|
-
return { files: matched.slice(0, 100), searchDir: searchPath, totalFound };
|
|
1744
|
-
} catch (error) {
|
|
1745
|
-
return {
|
|
1746
|
-
files: [],
|
|
1747
|
-
searchDir: searchPath,
|
|
1748
|
-
totalFound: 0,
|
|
1749
|
-
error: `[GLOB ERROR] ${error instanceof Error ? error.message : String(error)}`
|
|
1750
|
-
};
|
|
1751
|
-
}
|
|
1752
|
-
}
|
|
1753
1765
|
};
|
|
1754
1766
|
|
|
1755
1767
|
// tools/warp_grep/providers/code_storage_http.ts
|
|
@@ -1776,9 +1788,9 @@ function createCodeStorageHttpCommands(config) {
|
|
|
1776
1788
|
const { baseUrl, repoId, branch } = config;
|
|
1777
1789
|
const encodedRepoId = encodeURIComponent(repoId);
|
|
1778
1790
|
return {
|
|
1779
|
-
grep: (pattern,
|
|
1780
|
-
read: (
|
|
1781
|
-
listDir: (
|
|
1791
|
+
grep: (pattern, path5, glob) => post(`${baseUrl}/api/code-search/${encodedRepoId}/grep`, { pattern, path: path5, glob, branch }, "grep"),
|
|
1792
|
+
read: (path5, start, end) => post(`${baseUrl}/api/code-search/${encodedRepoId}/read`, { path: path5, start, end, branch }, "read"),
|
|
1793
|
+
listDir: (path5, maxDepth) => post(`${baseUrl}/api/code-search/${encodedRepoId}/list`, { path: path5, maxDepth, branch }, "list")
|
|
1782
1794
|
};
|
|
1783
1795
|
}
|
|
1784
1796
|
|