@morphllm/morphsdk 0.2.146 → 0.2.148
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-FYO46OT6.js → chunk-33CVSDM6.js} +2 -2
- package/dist/{chunk-SJYAKVSS.js → chunk-44WJK4MN.js} +2 -2
- package/dist/{chunk-SJYAKVSS.js.map → chunk-44WJK4MN.js.map} +1 -1
- package/dist/{chunk-GJUB3ECP.js → chunk-4FLNLA2D.js} +2 -2
- package/dist/{chunk-E4YKEKGW.js → chunk-4K36ATXI.js} +2 -2
- package/dist/{chunk-V73GO5AJ.js → chunk-4ZA4INTU.js} +2 -2
- package/dist/{chunk-T564HFSH.js → chunk-5R5FVUAL.js} +1 -1
- package/dist/{chunk-FBOJJ3UY.js → chunk-6MVJ5J6H.js} +17 -17
- package/dist/{chunk-UVNENJ6H.js → chunk-7PZKCKIH.js} +3 -3
- package/dist/{chunk-NF2QWJDY.js → chunk-B3AKP3RA.js} +31 -2
- package/dist/chunk-B3AKP3RA.js.map +1 -0
- package/dist/chunk-CMSHXALI.js +60 -0
- package/dist/chunk-CMSHXALI.js.map +1 -0
- package/dist/{chunk-I7SFRYTX.js → chunk-DMYFFSLS.js} +2 -2
- package/dist/{chunk-Q36MNOFA.js → chunk-E5K4VHMA.js} +2 -2
- package/dist/{chunk-OV57JBMB.js → chunk-EGUJNHMV.js} +2 -2
- package/dist/{chunk-E45FW5EK.js → chunk-ELU34VFH.js} +2 -2
- package/dist/{chunk-BDHKL3MT.js → chunk-F6OUFO25.js} +2 -2
- package/dist/{chunk-QRSWXP4K.js → chunk-H6TH3F4V.js} +2 -2
- package/dist/{chunk-FIVYDIHX.js → chunk-HYRHI2UL.js} +1 -1
- package/dist/{chunk-DKODF3YG.js → chunk-I3J46TSB.js} +5 -4
- package/dist/chunk-I3J46TSB.js.map +1 -0
- package/dist/{chunk-J2HIK4GB.js → chunk-IL7OLRJP.js} +2 -2
- package/dist/{chunk-JSWNBCGS.js → chunk-LJM3R7UZ.js} +2 -2
- package/dist/{chunk-NKUSUSVI.js → chunk-N67TYW2Z.js} +3 -3
- package/dist/chunk-OBXWT7NJ.js +401 -0
- package/dist/chunk-OBXWT7NJ.js.map +1 -0
- package/dist/{chunk-MMBQKN4G.js → chunk-OHYA6SUF.js} +2 -2
- package/dist/{chunk-EU7OLX4Z.js → chunk-RH2JB76E.js} +2 -2
- package/dist/{chunk-KYKRRF7E.js → chunk-SID7EXWK.js} +2 -2
- package/dist/{chunk-YIETFYCL.js → chunk-T7HF2TDQ.js} +75 -48
- package/dist/chunk-T7HF2TDQ.js.map +1 -0
- package/dist/{chunk-VZ6VYRQB.js → chunk-TSUTRT4Q.js} +2 -2
- package/dist/{chunk-UYPWKQKV.js → chunk-TVUYMM4J.js} +2 -2
- package/dist/{chunk-HZOTLGJH.js → chunk-XNBQJSLI.js} +42 -2
- package/dist/chunk-XNBQJSLI.js.map +1 -0
- package/dist/{chunk-BIQ7234U.js → chunk-XT7JQAXV.js} +2 -2
- package/dist/{chunk-4PBUB77N.js → chunk-YWJHOYEM.js} +2 -2
- package/dist/client.cjs +439 -446
- package/dist/client.cjs.map +1 -1
- package/dist/client.js +26 -27
- package/dist/edge.cjs +1 -1
- package/dist/edge.cjs.map +1 -1
- package/dist/edge.js +4 -4
- package/dist/{finish-DBKuo8yj.d.ts → finish-Ddj1MPGt.d.ts} +1 -1
- package/dist/index.cjs +458 -446
- 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 +435 -442
- package/dist/subagents/anthropic.cjs.map +1 -1
- package/dist/subagents/anthropic.js +8 -9
- package/dist/subagents/vercel.cjs +435 -442
- package/dist/subagents/vercel.cjs.map +1 -1
- package/dist/subagents/vercel.js +8 -9
- package/dist/tools/browser/anthropic.cjs +1 -1
- package/dist/tools/browser/anthropic.cjs.map +1 -1
- package/dist/tools/browser/anthropic.js +5 -5
- package/dist/tools/browser/core.cjs +1 -1
- package/dist/tools/browser/core.cjs.map +1 -1
- package/dist/tools/browser/core.js +4 -4
- package/dist/tools/browser/index.cjs +1 -1
- package/dist/tools/browser/index.cjs.map +1 -1
- package/dist/tools/browser/index.js +7 -7
- package/dist/tools/browser/openai.cjs +1 -1
- package/dist/tools/browser/openai.cjs.map +1 -1
- package/dist/tools/browser/openai.js +5 -5
- package/dist/tools/browser/profiles/core.cjs +1 -1
- package/dist/tools/browser/profiles/core.cjs.map +1 -1
- package/dist/tools/browser/profiles/core.js +3 -3
- package/dist/tools/browser/profiles/index.cjs +1 -1
- package/dist/tools/browser/profiles/index.cjs.map +1 -1
- package/dist/tools/browser/profiles/index.js +3 -3
- package/dist/tools/browser/vercel.cjs +1 -1
- package/dist/tools/browser/vercel.cjs.map +1 -1
- package/dist/tools/browser/vercel.js +5 -5
- package/dist/tools/codebase_search/anthropic.cjs +1 -1
- package/dist/tools/codebase_search/anthropic.cjs.map +1 -1
- package/dist/tools/codebase_search/anthropic.js +4 -4
- package/dist/tools/codebase_search/core.cjs +1 -1
- package/dist/tools/codebase_search/core.cjs.map +1 -1
- package/dist/tools/codebase_search/core.js +3 -3
- package/dist/tools/codebase_search/index.cjs +1 -1
- package/dist/tools/codebase_search/index.cjs.map +1 -1
- package/dist/tools/codebase_search/index.js +6 -6
- package/dist/tools/codebase_search/openai.cjs +1 -1
- package/dist/tools/codebase_search/openai.cjs.map +1 -1
- package/dist/tools/codebase_search/openai.js +4 -4
- package/dist/tools/codebase_search/vercel.cjs +1 -1
- package/dist/tools/codebase_search/vercel.cjs.map +1 -1
- package/dist/tools/codebase_search/vercel.js +4 -4
- package/dist/tools/fastapply/anthropic.cjs +1 -1
- package/dist/tools/fastapply/anthropic.cjs.map +1 -1
- package/dist/tools/fastapply/anthropic.js +4 -4
- package/dist/tools/fastapply/apply.cjs +1 -1
- package/dist/tools/fastapply/apply.cjs.map +1 -1
- package/dist/tools/fastapply/apply.js +2 -2
- package/dist/tools/fastapply/core.cjs +1 -1
- package/dist/tools/fastapply/core.cjs.map +1 -1
- package/dist/tools/fastapply/core.js +3 -3
- package/dist/tools/fastapply/index.cjs +1 -1
- package/dist/tools/fastapply/index.cjs.map +1 -1
- package/dist/tools/fastapply/index.js +6 -6
- package/dist/tools/fastapply/openai.cjs +1 -1
- package/dist/tools/fastapply/openai.cjs.map +1 -1
- package/dist/tools/fastapply/openai.js +4 -4
- package/dist/tools/fastapply/vercel.cjs +1 -1
- package/dist/tools/fastapply/vercel.cjs.map +1 -1
- package/dist/tools/fastapply/vercel.js +4 -4
- package/dist/tools/index.cjs +1 -1
- package/dist/tools/index.cjs.map +1 -1
- package/dist/tools/index.js +6 -6
- package/dist/tools/utils/resilience.cjs +1 -1
- package/dist/tools/utils/resilience.cjs.map +1 -1
- package/dist/tools/utils/resilience.js +2 -2
- package/dist/tools/warp_grep/agent/config.cjs +4 -3
- package/dist/tools/warp_grep/agent/config.cjs.map +1 -1
- package/dist/tools/warp_grep/agent/config.d.ts +2 -1
- package/dist/tools/warp_grep/agent/config.js +1 -1
- package/dist/tools/warp_grep/agent/parser.cjs +52 -121
- package/dist/tools/warp_grep/agent/parser.cjs.map +1 -1
- package/dist/tools/warp_grep/agent/parser.d.ts +12 -5
- package/dist/tools/warp_grep/agent/parser.js +7 -3
- package/dist/tools/warp_grep/agent/runner.cjs +348 -424
- package/dist/tools/warp_grep/agent/runner.cjs.map +1 -1
- package/dist/tools/warp_grep/agent/runner.d.ts +6 -3
- package/dist/tools/warp_grep/agent/runner.js +5 -6
- package/dist/tools/warp_grep/agent/types.cjs.map +1 -1
- package/dist/tools/warp_grep/agent/types.d.ts +22 -3
- package/dist/tools/warp_grep/anthropic.cjs +435 -442
- package/dist/tools/warp_grep/anthropic.cjs.map +1 -1
- package/dist/tools/warp_grep/anthropic.js +8 -9
- package/dist/tools/warp_grep/client.cjs +435 -442
- package/dist/tools/warp_grep/client.cjs.map +1 -1
- package/dist/tools/warp_grep/client.js +7 -8
- package/dist/tools/warp_grep/gemini.cjs +435 -442
- package/dist/tools/warp_grep/gemini.cjs.map +1 -1
- package/dist/tools/warp_grep/gemini.js +7 -8
- package/dist/tools/warp_grep/gemini.js.map +1 -1
- package/dist/tools/warp_grep/harness.cjs +168 -180
- package/dist/tools/warp_grep/harness.cjs.map +1 -1
- package/dist/tools/warp_grep/harness.d.ts +17 -38
- package/dist/tools/warp_grep/harness.js +15 -14
- package/dist/tools/warp_grep/harness.js.map +1 -1
- package/dist/tools/warp_grep/index.cjs +454 -442
- 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 +435 -442
- package/dist/tools/warp_grep/openai.cjs.map +1 -1
- package/dist/tools/warp_grep/openai.js +8 -9
- package/dist/tools/warp_grep/providers/local.cjs +43 -2
- package/dist/tools/warp_grep/providers/local.cjs.map +1 -1
- package/dist/tools/warp_grep/providers/local.d.ts +5 -1
- package/dist/tools/warp_grep/providers/local.js +2 -2
- package/dist/tools/warp_grep/providers/remote.cjs +32 -2
- package/dist/tools/warp_grep/providers/remote.cjs.map +1 -1
- package/dist/tools/warp_grep/providers/remote.d.ts +9 -1
- package/dist/tools/warp_grep/providers/remote.js +2 -2
- package/dist/tools/warp_grep/providers/types.cjs.map +1 -1
- package/dist/tools/warp_grep/providers/types.d.ts +14 -1
- package/dist/tools/warp_grep/vercel.cjs +435 -442
- package/dist/tools/warp_grep/vercel.cjs.map +1 -1
- package/dist/tools/warp_grep/vercel.js +8 -9
- package/dist/version.cjs +1 -1
- package/dist/version.cjs.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-DKODF3YG.js.map +0 -1
- package/dist/chunk-EUHNJMWL.js +0 -409
- package/dist/chunk-EUHNJMWL.js.map +0 -1
- package/dist/chunk-HZOTLGJH.js.map +0 -1
- package/dist/chunk-NF2QWJDY.js.map +0 -1
- package/dist/chunk-VCKJ22DX.js +0 -131
- package/dist/chunk-VCKJ22DX.js.map +0 -1
- package/dist/chunk-YIETFYCL.js.map +0 -1
- /package/dist/{chunk-FYO46OT6.js.map → chunk-33CVSDM6.js.map} +0 -0
- /package/dist/{chunk-GJUB3ECP.js.map → chunk-4FLNLA2D.js.map} +0 -0
- /package/dist/{chunk-E4YKEKGW.js.map → chunk-4K36ATXI.js.map} +0 -0
- /package/dist/{chunk-V73GO5AJ.js.map → chunk-4ZA4INTU.js.map} +0 -0
- /package/dist/{chunk-T564HFSH.js.map → chunk-5R5FVUAL.js.map} +0 -0
- /package/dist/{chunk-FBOJJ3UY.js.map → chunk-6MVJ5J6H.js.map} +0 -0
- /package/dist/{chunk-UVNENJ6H.js.map → chunk-7PZKCKIH.js.map} +0 -0
- /package/dist/{chunk-I7SFRYTX.js.map → chunk-DMYFFSLS.js.map} +0 -0
- /package/dist/{chunk-Q36MNOFA.js.map → chunk-E5K4VHMA.js.map} +0 -0
- /package/dist/{chunk-OV57JBMB.js.map → chunk-EGUJNHMV.js.map} +0 -0
- /package/dist/{chunk-E45FW5EK.js.map → chunk-ELU34VFH.js.map} +0 -0
- /package/dist/{chunk-BDHKL3MT.js.map → chunk-F6OUFO25.js.map} +0 -0
- /package/dist/{chunk-QRSWXP4K.js.map → chunk-H6TH3F4V.js.map} +0 -0
- /package/dist/{chunk-FIVYDIHX.js.map → chunk-HYRHI2UL.js.map} +0 -0
- /package/dist/{chunk-J2HIK4GB.js.map → chunk-IL7OLRJP.js.map} +0 -0
- /package/dist/{chunk-JSWNBCGS.js.map → chunk-LJM3R7UZ.js.map} +0 -0
- /package/dist/{chunk-NKUSUSVI.js.map → chunk-N67TYW2Z.js.map} +0 -0
- /package/dist/{chunk-MMBQKN4G.js.map → chunk-OHYA6SUF.js.map} +0 -0
- /package/dist/{chunk-EU7OLX4Z.js.map → chunk-RH2JB76E.js.map} +0 -0
- /package/dist/{chunk-KYKRRF7E.js.map → chunk-SID7EXWK.js.map} +0 -0
- /package/dist/{chunk-VZ6VYRQB.js.map → chunk-TSUTRT4Q.js.map} +0 -0
- /package/dist/{chunk-UYPWKQKV.js.map → chunk-TVUYMM4J.js.map} +0 -0
- /package/dist/{chunk-BIQ7234U.js.map → chunk-XT7JQAXV.js.map} +0 -0
- /package/dist/{chunk-4PBUB77N.js.map → chunk-YWJHOYEM.js.map} +0 -0
|
@@ -41,11 +41,12 @@ var init_config = __esm({
|
|
|
41
41
|
return isNaN(parsed) || parsed <= 0 ? defaultMs : parsed;
|
|
42
42
|
};
|
|
43
43
|
AGENT_CONFIG = {
|
|
44
|
-
MAX_TURNS:
|
|
44
|
+
MAX_TURNS: 6,
|
|
45
45
|
/** Default timeout for model calls. Can be overridden via MORPH_WARP_GREP_TIMEOUT env var (in ms) */
|
|
46
46
|
TIMEOUT_MS: parseEnvTimeout(process.env.MORPH_WARP_GREP_TIMEOUT, 3e4),
|
|
47
|
-
MAX_CONTEXT_CHARS:
|
|
47
|
+
MAX_CONTEXT_CHARS: 321600,
|
|
48
48
|
MAX_OUTPUT_LINES: 200,
|
|
49
|
+
MAX_LIST_RESULTS: 500,
|
|
49
50
|
MAX_READ_LINES: 800,
|
|
50
51
|
MAX_LIST_DEPTH: 3,
|
|
51
52
|
LIST_TIMEOUT_MS: 2e3
|
|
@@ -130,7 +131,7 @@ var init_config = __esm({
|
|
|
130
131
|
".*"
|
|
131
132
|
];
|
|
132
133
|
DEFAULT_EXCLUDES = (process.env.MORPH_WARP_GREP_EXCLUDE || "").split(",").map((s) => s.trim()).filter(Boolean).concat(BUILTIN_EXCLUDES);
|
|
133
|
-
DEFAULT_MODEL = "morph-warp-grep-v2";
|
|
134
|
+
DEFAULT_MODEL = "morph-warp-grep-v2.1";
|
|
134
135
|
}
|
|
135
136
|
});
|
|
136
137
|
|
|
@@ -203,19 +204,19 @@ var init_ripgrep = __esm({
|
|
|
203
204
|
|
|
204
205
|
// tools/warp_grep/utils/paths.ts
|
|
205
206
|
function resolveUnderRepo(repoRoot, targetPath) {
|
|
206
|
-
const absRoot =
|
|
207
|
-
const resolved =
|
|
207
|
+
const absRoot = import_path4.default.resolve(repoRoot);
|
|
208
|
+
const resolved = import_path4.default.resolve(absRoot, targetPath);
|
|
208
209
|
ensureWithinRepo(absRoot, resolved);
|
|
209
210
|
return resolved;
|
|
210
211
|
}
|
|
211
212
|
function ensureWithinRepo(repoRoot, absTarget) {
|
|
212
|
-
const rel =
|
|
213
|
-
if (rel.startsWith("..") ||
|
|
213
|
+
const rel = import_path4.default.relative(import_path4.default.resolve(repoRoot), import_path4.default.resolve(absTarget));
|
|
214
|
+
if (rel.startsWith("..") || import_path4.default.isAbsolute(rel)) {
|
|
214
215
|
throw new Error(`Path outside repository root: ${absTarget}`);
|
|
215
216
|
}
|
|
216
217
|
}
|
|
217
218
|
function toRepoRelative(repoRoot, absPath) {
|
|
218
|
-
return
|
|
219
|
+
return import_path4.default.relative(import_path4.default.resolve(repoRoot), import_path4.default.resolve(absPath));
|
|
219
220
|
}
|
|
220
221
|
function isSymlink(p) {
|
|
221
222
|
try {
|
|
@@ -226,7 +227,7 @@ function isSymlink(p) {
|
|
|
226
227
|
}
|
|
227
228
|
}
|
|
228
229
|
function fixPathRepetition(fullPath) {
|
|
229
|
-
const segments = fullPath.split(
|
|
230
|
+
const segments = fullPath.split(import_path4.default.sep).filter(Boolean);
|
|
230
231
|
if (segments.length < 2) return null;
|
|
231
232
|
for (let len = Math.floor(segments.length / 2); len >= 1; len--) {
|
|
232
233
|
for (let i = 0; i <= segments.length - 2 * len; i++) {
|
|
@@ -234,7 +235,7 @@ function fixPathRepetition(fullPath) {
|
|
|
234
235
|
const second = segments.slice(i + len, i + 2 * len);
|
|
235
236
|
if (first.every((seg, idx) => seg === second[idx])) {
|
|
236
237
|
const fixed = [...segments.slice(0, i), ...segments.slice(i + len)];
|
|
237
|
-
return
|
|
238
|
+
return import_path4.default.sep + fixed.join(import_path4.default.sep);
|
|
238
239
|
}
|
|
239
240
|
}
|
|
240
241
|
}
|
|
@@ -258,12 +259,12 @@ function isTextualFile(filePath, maxBytes = 2e6) {
|
|
|
258
259
|
return false;
|
|
259
260
|
}
|
|
260
261
|
}
|
|
261
|
-
var import_fs,
|
|
262
|
+
var import_fs, import_path4;
|
|
262
263
|
var init_paths = __esm({
|
|
263
264
|
"tools/warp_grep/utils/paths.ts"() {
|
|
264
265
|
"use strict";
|
|
265
266
|
import_fs = __toESM(require("fs"), 1);
|
|
266
|
-
|
|
267
|
+
import_path4 = __toESM(require("path"), 1);
|
|
267
268
|
}
|
|
268
269
|
});
|
|
269
270
|
|
|
@@ -294,12 +295,12 @@ function shouldSkip2(name, allowNames) {
|
|
|
294
295
|
}
|
|
295
296
|
return false;
|
|
296
297
|
}
|
|
297
|
-
var import_promises2,
|
|
298
|
+
var import_promises2, import_path5, SKIP_NAMES2, SKIP_EXTENSIONS2, LocalRipgrepProvider;
|
|
298
299
|
var init_local = __esm({
|
|
299
300
|
"tools/warp_grep/providers/local.ts"() {
|
|
300
301
|
"use strict";
|
|
301
302
|
import_promises2 = __toESM(require("fs/promises"), 1);
|
|
302
|
-
|
|
303
|
+
import_path5 = __toESM(require("path"), 1);
|
|
303
304
|
init_ripgrep();
|
|
304
305
|
init_paths();
|
|
305
306
|
init_files();
|
|
@@ -401,7 +402,7 @@ var init_local = __esm({
|
|
|
401
402
|
}
|
|
402
403
|
const stat = await import_promises2.default.stat(abs).catch(() => null);
|
|
403
404
|
if (!stat) return { lines: [] };
|
|
404
|
-
const targetArg = abs ===
|
|
405
|
+
const targetArg = abs === import_path5.default.resolve(this.repoRoot) ? "." : toRepoRelative(this.repoRoot, abs);
|
|
405
406
|
const contextLines = params.context_lines !== void 0 ? String(params.context_lines) : "1";
|
|
406
407
|
const args = [
|
|
407
408
|
"--no-config",
|
|
@@ -556,7 +557,7 @@ Details: ${res.stderr}` : ""}`
|
|
|
556
557
|
if (timedOut || results.length >= maxResults) break;
|
|
557
558
|
if (shouldSkip2(entry.name, allowNames)) continue;
|
|
558
559
|
if (regex && !regex.test(entry.name)) continue;
|
|
559
|
-
const full =
|
|
560
|
+
const full = import_path5.default.join(dir, entry.name);
|
|
560
561
|
const isDir = entry.isDirectory();
|
|
561
562
|
results.push({
|
|
562
563
|
name: entry.name,
|
|
@@ -572,6 +573,46 @@ Details: ${res.stderr}` : ""}`
|
|
|
572
573
|
await walk(abs, 0);
|
|
573
574
|
return results;
|
|
574
575
|
}
|
|
576
|
+
async glob(params) {
|
|
577
|
+
let abs;
|
|
578
|
+
try {
|
|
579
|
+
abs = params.path ? resolveUnderRepo(this.repoRoot, params.path) : this.repoRoot;
|
|
580
|
+
} catch (err) {
|
|
581
|
+
return { files: [], searchDir: this.repoRoot, totalFound: 0, error: `[PATH ERROR] ${err instanceof Error ? err.message : String(err)}` };
|
|
582
|
+
}
|
|
583
|
+
const stat = await import_promises2.default.stat(abs).catch(() => null);
|
|
584
|
+
if (!stat || !stat.isDirectory()) {
|
|
585
|
+
return { files: [], searchDir: abs, totalFound: 0, error: `[PATH ERROR] Directory not found: ${params.path || "."}` };
|
|
586
|
+
}
|
|
587
|
+
const targetArg = abs === import_path5.default.resolve(this.repoRoot) ? "." : toRepoRelative(this.repoRoot, abs);
|
|
588
|
+
const args = [
|
|
589
|
+
"--no-config",
|
|
590
|
+
"--files",
|
|
591
|
+
"--color=never",
|
|
592
|
+
"-g",
|
|
593
|
+
params.pattern,
|
|
594
|
+
...this.excludes.filter((e) => !this.allowNames?.has(e)).flatMap((e) => ["-g", `!${e}`]),
|
|
595
|
+
targetArg || "."
|
|
596
|
+
];
|
|
597
|
+
const res = await runRipgrep(args, { cwd: this.repoRoot });
|
|
598
|
+
if (res.exitCode === -1) {
|
|
599
|
+
return { files: [], searchDir: abs, totalFound: 0, error: `[RIPGREP NOT AVAILABLE] ripgrep (rg) is required for glob search.` };
|
|
600
|
+
}
|
|
601
|
+
if (res.exitCode !== 0 && res.exitCode !== 1) {
|
|
602
|
+
return { files: [], searchDir: abs, totalFound: 0, error: `[GLOB ERROR] glob failed with exit code ${res.exitCode}${res.stderr ? `: ${res.stderr}` : ""}` };
|
|
603
|
+
}
|
|
604
|
+
const absRoot = import_path5.default.resolve(this.repoRoot);
|
|
605
|
+
const relFiles = (res.stdout || "").trim().split(/\r?\n/).filter((l) => l.length > 0);
|
|
606
|
+
const absFiles = relFiles.map((f) => import_path5.default.resolve(absRoot, f));
|
|
607
|
+
const withMtime = [];
|
|
608
|
+
for (const f of absFiles) {
|
|
609
|
+
const s = await import_promises2.default.stat(f).catch(() => null);
|
|
610
|
+
withMtime.push({ file: f, mtime: s?.mtimeMs ?? 0 });
|
|
611
|
+
}
|
|
612
|
+
withMtime.sort((a, b) => b.mtime - a.mtime);
|
|
613
|
+
const totalFound = withMtime.length;
|
|
614
|
+
return { files: withMtime.slice(0, 100).map((f) => f.file), searchDir: abs, totalFound };
|
|
615
|
+
}
|
|
575
616
|
};
|
|
576
617
|
}
|
|
577
618
|
});
|
|
@@ -594,131 +635,58 @@ module.exports = __toCommonJS(gemini_exports);
|
|
|
594
635
|
init_config();
|
|
595
636
|
|
|
596
637
|
// tools/warp_grep/agent/parser.ts
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
638
|
+
function parseReadLines(linesStr) {
|
|
639
|
+
const ranges = [];
|
|
640
|
+
for (const rangeStr of linesStr.split(",")) {
|
|
641
|
+
const trimmed = rangeStr.trim();
|
|
642
|
+
if (!trimmed) continue;
|
|
643
|
+
const parts = trimmed.split("-").map((v) => parseInt(v.trim(), 10));
|
|
644
|
+
if (parts.length >= 2 && Number.isFinite(parts[0]) && Number.isFinite(parts[1])) {
|
|
645
|
+
ranges.push([parts[0], parts[1]]);
|
|
646
|
+
} else if (Number.isFinite(parts[0])) {
|
|
647
|
+
ranges.push([parts[0], parts[0]]);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
if (ranges.length === 1) return { start: ranges[0][0], end: ranges[0][1] };
|
|
651
|
+
if (ranges.length > 1) return { lines: ranges };
|
|
652
|
+
return {};
|
|
600
653
|
}
|
|
601
|
-
function
|
|
602
|
-
const
|
|
603
|
-
const
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
const
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
const paramRegex = /<parameter=([a-z_][a-z0-9_]*)>([\s\S]*?)<\/parameter>/gi;
|
|
611
|
-
let paramMatch;
|
|
612
|
-
while ((paramMatch = paramRegex.exec(body)) !== null) {
|
|
613
|
-
params[paramMatch[1].toLowerCase()] = paramMatch[2].trim();
|
|
654
|
+
function parseFinishFiles(filesStr) {
|
|
655
|
+
const files = [];
|
|
656
|
+
for (const line of filesStr.trim().split(/\s+/)) {
|
|
657
|
+
const trimmed = line.trim();
|
|
658
|
+
if (!trimmed) continue;
|
|
659
|
+
const colonIdx = trimmed.indexOf(":");
|
|
660
|
+
if (colonIdx === -1) {
|
|
661
|
+
files.push({ path: trimmed, lines: "*" });
|
|
662
|
+
continue;
|
|
614
663
|
}
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
if (!directPath && command) {
|
|
631
|
-
const tokens = command.trim().split(/\s+/);
|
|
632
|
-
const pathTokens = tokens.slice(1).filter((t) => !t.startsWith("-") && !t.startsWith("|") && !t.startsWith("\\("));
|
|
633
|
-
if (pathTokens.length > 0) {
|
|
634
|
-
dirPath = pathTokens[0];
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
|
-
tools.push({ name: "list_directory", arguments: { path: dirPath, pattern: params.pattern || null } });
|
|
638
|
-
} else if (funcName === "read") {
|
|
639
|
-
const filePath = params.path;
|
|
640
|
-
if (!filePath) continue;
|
|
641
|
-
const args = { path: filePath };
|
|
642
|
-
const linesStr = params.lines;
|
|
643
|
-
if (linesStr) {
|
|
644
|
-
const ranges = [];
|
|
645
|
-
for (const rangeStr of linesStr.split(",")) {
|
|
646
|
-
const trimmed = rangeStr.trim();
|
|
647
|
-
if (!trimmed) continue;
|
|
648
|
-
const [s, e] = trimmed.split("-").map((v) => parseInt(v.trim(), 10));
|
|
649
|
-
if (Number.isFinite(s) && Number.isFinite(e)) {
|
|
650
|
-
ranges.push([s, e]);
|
|
651
|
-
} else if (Number.isFinite(s)) {
|
|
652
|
-
ranges.push([s, s]);
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
if (ranges.length === 1) {
|
|
656
|
-
args.start = ranges[0][0];
|
|
657
|
-
args.end = ranges[0][1];
|
|
658
|
-
} else if (ranges.length > 1) {
|
|
659
|
-
args.lines = ranges;
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
tools.push({ name: "read", arguments: args });
|
|
663
|
-
} else if (funcName === "finish") {
|
|
664
|
-
if (params.result && !params.files) {
|
|
665
|
-
tools.push({ name: "finish", arguments: { files: [], textResult: params.result } });
|
|
666
|
-
continue;
|
|
667
|
-
}
|
|
668
|
-
const filesStr = params.files;
|
|
669
|
-
if (!filesStr) {
|
|
670
|
-
tools.push({ name: "finish", arguments: { files: [], textResult: "No relevant code found." } });
|
|
671
|
-
continue;
|
|
672
|
-
}
|
|
673
|
-
const files = [];
|
|
674
|
-
for (const line of filesStr.split("\n")) {
|
|
675
|
-
const trimmed = line.trim();
|
|
676
|
-
if (!trimmed) continue;
|
|
677
|
-
const colonIdx = trimmed.indexOf(":");
|
|
678
|
-
if (colonIdx === -1) {
|
|
679
|
-
files.push({ path: trimmed, lines: "*" });
|
|
680
|
-
} else {
|
|
681
|
-
const filePath = trimmed.slice(0, colonIdx);
|
|
682
|
-
const rangesPart = trimmed.slice(colonIdx + 1);
|
|
683
|
-
const ranges = [];
|
|
684
|
-
for (const rangeStr of rangesPart.split(",")) {
|
|
685
|
-
const rt = rangeStr.trim();
|
|
686
|
-
if (!rt || rt === "*") {
|
|
687
|
-
files.push({ path: filePath, lines: "*" });
|
|
688
|
-
break;
|
|
689
|
-
}
|
|
690
|
-
const [s, e] = rt.split("-").map((v) => parseInt(v.trim(), 10));
|
|
691
|
-
if (Number.isFinite(s) && Number.isFinite(e)) {
|
|
692
|
-
ranges.push([s, e]);
|
|
693
|
-
} else if (Number.isFinite(s)) {
|
|
694
|
-
ranges.push([s, s]);
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
if (ranges.length > 0) {
|
|
698
|
-
files.push({ path: filePath, lines: ranges });
|
|
699
|
-
} else if (!files.some((f) => f.path === filePath)) {
|
|
700
|
-
files.push({ path: filePath, lines: "*" });
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
if (files.length > 0) {
|
|
705
|
-
tools.push({ name: "finish", arguments: { files } });
|
|
706
|
-
} else {
|
|
707
|
-
tools.push({ name: "finish", arguments: { files: [], textResult: filesStr } });
|
|
664
|
+
const filePath = trimmed.slice(0, colonIdx);
|
|
665
|
+
const rangesPart = trimmed.slice(colonIdx + 1);
|
|
666
|
+
if (!rangesPart.trim() || rangesPart.trim() === "*") {
|
|
667
|
+
files.push({ path: filePath, lines: "*" });
|
|
668
|
+
continue;
|
|
669
|
+
}
|
|
670
|
+
const ranges = [];
|
|
671
|
+
for (const rangeStr of rangesPart.split(",")) {
|
|
672
|
+
const rt = rangeStr.trim();
|
|
673
|
+
if (!rt) continue;
|
|
674
|
+
const parts = rt.split("-").map((v) => parseInt(v.trim(), 10));
|
|
675
|
+
if (parts.length >= 2 && Number.isFinite(parts[0]) && Number.isFinite(parts[1])) {
|
|
676
|
+
ranges.push([parts[0], parts[1]]);
|
|
677
|
+
} else if (Number.isFinite(parts[0])) {
|
|
678
|
+
ranges.push([parts[0], parts[0]]);
|
|
708
679
|
}
|
|
709
680
|
}
|
|
681
|
+
files.push({ path: filePath, lines: ranges.length > 0 ? ranges : "*" });
|
|
710
682
|
}
|
|
711
|
-
return
|
|
683
|
+
return files;
|
|
684
|
+
}
|
|
685
|
+
function extractPathFromCommand(command) {
|
|
686
|
+
const tokens = command.trim().split(/\s+/);
|
|
687
|
+
const pathTokens = tokens.slice(1).filter((t) => !t.startsWith("-") && !t.startsWith("|") && !t.startsWith("\\("));
|
|
688
|
+
return pathTokens[0] || ".";
|
|
712
689
|
}
|
|
713
|
-
var LLMResponseParser = class {
|
|
714
|
-
parse(text) {
|
|
715
|
-
if (typeof text !== "string") {
|
|
716
|
-
throw new TypeError("Command text must be a string.");
|
|
717
|
-
}
|
|
718
|
-
const withoutThink = text.replace(/<think>[\s\S]*?<\/think>/gi, "");
|
|
719
|
-
return parseQwen3ToolCalls(withoutThink);
|
|
720
|
-
}
|
|
721
|
-
};
|
|
722
690
|
|
|
723
691
|
// tools/warp_grep/agent/tools/grep.ts
|
|
724
692
|
async function toolGrep(provider, args) {
|
|
@@ -769,29 +737,42 @@ async function toolRead(provider, args) {
|
|
|
769
737
|
|
|
770
738
|
// tools/warp_grep/agent/tools/list_directory.ts
|
|
771
739
|
init_config();
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
const
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
740
|
+
var import_path = __toESM(require("path"), 1);
|
|
741
|
+
async function toolListDirectory(provider, args, repoRoot) {
|
|
742
|
+
const maxResults = args.maxResults ?? AGENT_CONFIG.MAX_LIST_RESULTS;
|
|
743
|
+
const maxDepth = args.maxDepth ?? AGENT_CONFIG.MAX_LIST_DEPTH;
|
|
744
|
+
const entries = await provider.listDirectory({
|
|
745
|
+
path: args.path,
|
|
746
|
+
pattern: args.pattern ?? null,
|
|
747
|
+
maxResults,
|
|
748
|
+
maxDepth
|
|
749
|
+
});
|
|
750
|
+
if (!entries.length) return "empty";
|
|
751
|
+
if (repoRoot) {
|
|
752
|
+
const absRoot = import_path.default.resolve(repoRoot);
|
|
753
|
+
const lines = entries.map((e) => import_path.default.join(absRoot, e.path));
|
|
754
|
+
return lines.join("\n");
|
|
755
|
+
}
|
|
756
|
+
return entries.map((e) => e.path).join("\n");
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
// tools/warp_grep/agent/tools/glob.ts
|
|
760
|
+
async function toolGlob(provider, args) {
|
|
761
|
+
const res = await provider.glob(args);
|
|
762
|
+
if (res.error) {
|
|
763
|
+
return res.error;
|
|
764
|
+
}
|
|
765
|
+
if (!res.files.length) {
|
|
766
|
+
return "no matches";
|
|
767
|
+
}
|
|
768
|
+
const header = `Found ${res.totalFound} file(s) matching "${args.pattern}" within ${res.searchDir}, sorted by modification time (newest first):`;
|
|
769
|
+
const body = res.files.join("\n");
|
|
770
|
+
const truncated = res.totalFound > res.files.length ? `
|
|
771
|
+
[${res.totalFound - res.files.length} files truncated]` : "";
|
|
772
|
+
return `${header}
|
|
773
|
+
---
|
|
774
|
+
${body}
|
|
775
|
+
---${truncated}`;
|
|
795
776
|
}
|
|
796
777
|
|
|
797
778
|
// tools/warp_grep/agent/tools/finish.ts
|
|
@@ -852,32 +833,21 @@ function mergeRanges(ranges) {
|
|
|
852
833
|
return merged;
|
|
853
834
|
}
|
|
854
835
|
|
|
855
|
-
// tools/warp_grep/agent/formatter.ts
|
|
856
|
-
var ToolOutputFormatter = class {
|
|
857
|
-
format(toolName, _args, output, options = {}) {
|
|
858
|
-
const name = (toolName ?? "").trim();
|
|
859
|
-
if (!name) {
|
|
860
|
-
return "";
|
|
861
|
-
}
|
|
862
|
-
const payload = output?.toString?.()?.trim?.() ?? "";
|
|
863
|
-
const isError = Boolean(options.isError);
|
|
864
|
-
if (!payload && !isError) {
|
|
865
|
-
return "";
|
|
866
|
-
}
|
|
867
|
-
return `<tool_response>
|
|
868
|
-
${payload}
|
|
869
|
-
</tool_response>`;
|
|
870
|
-
}
|
|
871
|
-
};
|
|
872
|
-
var sharedFormatter = new ToolOutputFormatter();
|
|
873
|
-
function formatAgentToolOutput(toolName, args, output, options = {}) {
|
|
874
|
-
return sharedFormatter.format(toolName, args, output, options);
|
|
875
|
-
}
|
|
876
|
-
|
|
877
836
|
// tools/warp_grep/agent/helpers.ts
|
|
878
|
-
var
|
|
837
|
+
var import_path2 = __toESM(require("path"), 1);
|
|
879
838
|
init_config();
|
|
880
839
|
var TRUNCATED_MARKER = "[truncated for context limit]";
|
|
840
|
+
function getMessageSize(m) {
|
|
841
|
+
if (m.role === "tool") return m.content.length;
|
|
842
|
+
if (m.role === "assistant") {
|
|
843
|
+
let size = typeof m.content === "string" ? m.content.length : 0;
|
|
844
|
+
if (m.tool_calls) {
|
|
845
|
+
size += m.tool_calls.reduce((s, tc) => s + tc.function.name.length + tc.function.arguments.length, 0);
|
|
846
|
+
}
|
|
847
|
+
return size;
|
|
848
|
+
}
|
|
849
|
+
return m.content.length;
|
|
850
|
+
}
|
|
881
851
|
function formatTurnMessage(turnsUsed, maxTurns) {
|
|
882
852
|
const turnsRemaining = maxTurns - turnsUsed;
|
|
883
853
|
if (turnsRemaining === 1) {
|
|
@@ -888,33 +858,30 @@ You have used ${turnsUsed} turns, you only have 1 turn remaining. You have run o
|
|
|
888
858
|
You have used ${turnsUsed} turn${turnsUsed === 1 ? "" : "s"} and have ${turnsRemaining} remaining`;
|
|
889
859
|
}
|
|
890
860
|
function calculateContextBudget(messages) {
|
|
891
|
-
const totalChars = messages.reduce((sum, m) => sum + m
|
|
861
|
+
const totalChars = messages.reduce((sum, m) => sum + getMessageSize(m), 0);
|
|
892
862
|
const maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS;
|
|
893
|
-
const percent = Math.
|
|
894
|
-
const usedK = Math.
|
|
895
|
-
const maxK = Math.
|
|
896
|
-
return `<context_budget>${percent}% (${usedK}K/${maxK}K chars
|
|
863
|
+
const percent = Math.floor(totalChars / maxChars * 100);
|
|
864
|
+
const usedK = Math.floor(totalChars / 1e3);
|
|
865
|
+
const maxK = Math.floor(maxChars / 1e3);
|
|
866
|
+
return `<context_budget>${percent}% (${usedK}K/${maxK}K chars)</context_budget>`;
|
|
897
867
|
}
|
|
898
868
|
async function buildInitialState(repoRoot, searchTerm, provider, options) {
|
|
899
869
|
const budget = calculateContextBudget([]);
|
|
900
|
-
const turnTag = `
|
|
870
|
+
const turnTag = `You have used 0 turns and have ${AGENT_CONFIG.MAX_TURNS} remaining`;
|
|
901
871
|
const treeDepth = options?.search_type === "node_modules" ? 1 : 2;
|
|
872
|
+
const absRoot = import_path2.default.resolve(repoRoot);
|
|
902
873
|
try {
|
|
903
874
|
const entries = await provider.listDirectory({
|
|
904
875
|
path: ".",
|
|
905
876
|
maxResults: AGENT_CONFIG.MAX_OUTPUT_LINES,
|
|
906
877
|
maxDepth: treeDepth
|
|
907
878
|
});
|
|
908
|
-
const
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
});
|
|
913
|
-
const repoName = import_path.default.basename(repoRoot);
|
|
914
|
-
const treeOutput = treeLines.length > 0 ? `${repoName}/
|
|
915
|
-
${treeLines.join("\n")}` : `${repoName}/`;
|
|
879
|
+
const lines = [absRoot];
|
|
880
|
+
for (const e of entries) {
|
|
881
|
+
lines.push(import_path2.default.join(absRoot, e.path));
|
|
882
|
+
}
|
|
916
883
|
return `<repo_structure>
|
|
917
|
-
${
|
|
884
|
+
${lines.join("\n")}
|
|
918
885
|
</repo_structure>
|
|
919
886
|
|
|
920
887
|
<search_string>
|
|
@@ -923,9 +890,8 @@ ${searchTerm}
|
|
|
923
890
|
${budget}
|
|
924
891
|
${turnTag}`;
|
|
925
892
|
} catch {
|
|
926
|
-
const repoName = import_path.default.basename(repoRoot);
|
|
927
893
|
return `<repo_structure>
|
|
928
|
-
${
|
|
894
|
+
${absRoot}
|
|
929
895
|
</repo_structure>
|
|
930
896
|
|
|
931
897
|
<search_string>
|
|
@@ -936,26 +902,32 @@ ${turnTag}`;
|
|
|
936
902
|
}
|
|
937
903
|
}
|
|
938
904
|
function enforceContextLimit(messages, maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS) {
|
|
939
|
-
const getTotalChars = () => messages.reduce((sum, m) => sum + m
|
|
905
|
+
const getTotalChars = () => messages.reduce((sum, m) => sum + getMessageSize(m), 0);
|
|
940
906
|
if (getTotalChars() <= maxChars) {
|
|
941
907
|
return messages;
|
|
942
908
|
}
|
|
943
|
-
const
|
|
909
|
+
const truncatableIndices = [];
|
|
944
910
|
let firstUserSkipped = false;
|
|
945
911
|
for (let i = 0; i < messages.length; i++) {
|
|
946
|
-
|
|
912
|
+
const m = messages[i];
|
|
913
|
+
if (m.role === "tool") {
|
|
914
|
+
truncatableIndices.push(i);
|
|
915
|
+
} else if (m.role === "user") {
|
|
947
916
|
if (!firstUserSkipped) {
|
|
948
917
|
firstUserSkipped = true;
|
|
949
918
|
continue;
|
|
950
919
|
}
|
|
951
|
-
|
|
920
|
+
truncatableIndices.push(i);
|
|
952
921
|
}
|
|
953
922
|
}
|
|
954
|
-
for (const idx of
|
|
923
|
+
for (const idx of truncatableIndices) {
|
|
955
924
|
if (getTotalChars() <= maxChars) {
|
|
956
925
|
break;
|
|
957
926
|
}
|
|
958
|
-
|
|
927
|
+
const m = messages[idx];
|
|
928
|
+
if (m.role === "tool" && m.content !== TRUNCATED_MARKER) {
|
|
929
|
+
messages[idx] = { role: "tool", tool_call_id: m.tool_call_id, content: TRUNCATED_MARKER };
|
|
930
|
+
} else if (m.role === "user" && m.content !== TRUNCATED_MARKER) {
|
|
959
931
|
messages[idx] = { role: "user", content: TRUNCATED_MARKER };
|
|
960
932
|
}
|
|
961
933
|
}
|
|
@@ -968,7 +940,7 @@ var import_openai = __toESM(require("openai"), 1);
|
|
|
968
940
|
// package.json
|
|
969
941
|
var package_default = {
|
|
970
942
|
name: "@morphllm/morphsdk",
|
|
971
|
-
version: "0.2.
|
|
943
|
+
version: "0.2.148",
|
|
972
944
|
description: "TypeScript SDK and CLI for Morph Fast Apply integration",
|
|
973
945
|
type: "module",
|
|
974
946
|
main: "./dist/index.cjs",
|
|
@@ -1207,9 +1179,115 @@ var package_default = {
|
|
|
1207
1179
|
var SDK_VERSION = package_default.version;
|
|
1208
1180
|
|
|
1209
1181
|
// tools/warp_grep/agent/runner.ts
|
|
1210
|
-
var
|
|
1211
|
-
var parser = new LLMResponseParser();
|
|
1182
|
+
var import_path3 = __toESM(require("path"), 1);
|
|
1212
1183
|
var DEFAULT_API_URL = "https://api.morphllm.com";
|
|
1184
|
+
var TOOL_SPECS = [
|
|
1185
|
+
{
|
|
1186
|
+
type: "function",
|
|
1187
|
+
function: {
|
|
1188
|
+
name: "list_directory",
|
|
1189
|
+
description: "Execute ls or find commands to explore directory structure. Max 500 results. Common junk directories are excluded automatically.",
|
|
1190
|
+
parameters: {
|
|
1191
|
+
type: "object",
|
|
1192
|
+
properties: {
|
|
1193
|
+
command: {
|
|
1194
|
+
type: "string",
|
|
1195
|
+
description: "Full ls or find command (e.g. ls -la src/, find . -maxdepth 2 -type f -name '*.py', find . -type d, ls -d */)."
|
|
1196
|
+
}
|
|
1197
|
+
},
|
|
1198
|
+
required: ["command"]
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
},
|
|
1202
|
+
{
|
|
1203
|
+
type: "function",
|
|
1204
|
+
function: {
|
|
1205
|
+
name: "grep_search",
|
|
1206
|
+
description: "Search for a regex pattern in file contents. Returns matching lines with file paths and line numbers. Case-insensitive. Respects .gitignore.",
|
|
1207
|
+
parameters: {
|
|
1208
|
+
type: "object",
|
|
1209
|
+
properties: {
|
|
1210
|
+
pattern: {
|
|
1211
|
+
type: "string",
|
|
1212
|
+
description: "Regex pattern to search for in file contents (e.g. 'class\\s+\\w+Error', 'import|require|from', 'def (get|set|update)_user')."
|
|
1213
|
+
},
|
|
1214
|
+
path: {
|
|
1215
|
+
type: "string",
|
|
1216
|
+
description: "File or directory to search in. Defaults to current working directory."
|
|
1217
|
+
},
|
|
1218
|
+
glob: {
|
|
1219
|
+
type: "string",
|
|
1220
|
+
description: "Glob pattern to filter files (e.g. '*.py', '*.{ts,tsx,js,jsx,py,go}', 'src/**/*.go', '!*.test.*')."
|
|
1221
|
+
},
|
|
1222
|
+
limit: {
|
|
1223
|
+
type: "integer",
|
|
1224
|
+
description: "Limit output to first N matching lines. Shows all matches if not specified."
|
|
1225
|
+
}
|
|
1226
|
+
},
|
|
1227
|
+
required: ["pattern"]
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
},
|
|
1231
|
+
{
|
|
1232
|
+
type: "function",
|
|
1233
|
+
function: {
|
|
1234
|
+
name: "glob",
|
|
1235
|
+
description: "Find files by name/extension using glob patterns. Returns absolute paths sorted by modification time (newest first). Respects .gitignore. Max 100 results.",
|
|
1236
|
+
parameters: {
|
|
1237
|
+
type: "object",
|
|
1238
|
+
properties: {
|
|
1239
|
+
pattern: {
|
|
1240
|
+
type: "string",
|
|
1241
|
+
description: "Glob pattern to match files (e.g. '*.py', 'src/**/*.js', '*.{ts,tsx}', 'test_*.py')."
|
|
1242
|
+
},
|
|
1243
|
+
path: {
|
|
1244
|
+
type: "string",
|
|
1245
|
+
description: "Directory to search in. Defaults to repository root."
|
|
1246
|
+
}
|
|
1247
|
+
},
|
|
1248
|
+
required: ["pattern"]
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
},
|
|
1252
|
+
{
|
|
1253
|
+
type: "function",
|
|
1254
|
+
function: {
|
|
1255
|
+
name: "read",
|
|
1256
|
+
description: "Read entire files or specific line ranges using absolute paths.",
|
|
1257
|
+
parameters: {
|
|
1258
|
+
type: "object",
|
|
1259
|
+
properties: {
|
|
1260
|
+
path: {
|
|
1261
|
+
type: "string",
|
|
1262
|
+
description: "File path to read, using absolute path (e.g. '/home/ubuntu/repo/src/main.py' or windows path)."
|
|
1263
|
+
},
|
|
1264
|
+
lines: {
|
|
1265
|
+
type: "string",
|
|
1266
|
+
description: "Optional line range (e.g. '1-50' or '1-20,45-80'). Omit to read entire file."
|
|
1267
|
+
}
|
|
1268
|
+
},
|
|
1269
|
+
required: ["path"]
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
},
|
|
1273
|
+
{
|
|
1274
|
+
type: "function",
|
|
1275
|
+
function: {
|
|
1276
|
+
name: "finish",
|
|
1277
|
+
description: "Submit final answer with all relevant code locations. Include imports and over-include rather than miss context.",
|
|
1278
|
+
parameters: {
|
|
1279
|
+
type: "object",
|
|
1280
|
+
properties: {
|
|
1281
|
+
files: {
|
|
1282
|
+
type: "string",
|
|
1283
|
+
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."
|
|
1284
|
+
}
|
|
1285
|
+
},
|
|
1286
|
+
required: ["files"]
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
];
|
|
1213
1291
|
async function callModel(messages, model, options = {}) {
|
|
1214
1292
|
const baseUrl = options.morphApiUrl || DEFAULT_API_URL;
|
|
1215
1293
|
const apiKey = options.morphApiKey || process.env.MORPH_API_KEY || "";
|
|
@@ -1230,8 +1308,9 @@ async function callModel(messages, model, options = {}) {
|
|
|
1230
1308
|
data = await client.chat.completions.create({
|
|
1231
1309
|
model,
|
|
1232
1310
|
temperature: 0,
|
|
1233
|
-
max_tokens:
|
|
1311
|
+
max_tokens: 2048,
|
|
1234
1312
|
messages,
|
|
1313
|
+
tools: TOOL_SPECS,
|
|
1235
1314
|
...options.search_type ? { search_type: options.search_type } : {}
|
|
1236
1315
|
});
|
|
1237
1316
|
} catch (error) {
|
|
@@ -1243,187 +1322,87 @@ async function callModel(messages, model, options = {}) {
|
|
|
1243
1322
|
throw error;
|
|
1244
1323
|
}
|
|
1245
1324
|
const choice = data?.choices?.[0];
|
|
1246
|
-
const
|
|
1247
|
-
if (
|
|
1248
|
-
|
|
1325
|
+
const message = choice?.message;
|
|
1326
|
+
if (!message) {
|
|
1327
|
+
if (attempt === MAX_EMPTY_RETRIES) {
|
|
1328
|
+
throw new Error("Invalid response from model: no message in response");
|
|
1329
|
+
}
|
|
1330
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
1331
|
+
continue;
|
|
1332
|
+
}
|
|
1333
|
+
const toolCalls = (message.tool_calls || []).map((tc) => ({
|
|
1334
|
+
id: tc.id,
|
|
1335
|
+
type: "function",
|
|
1336
|
+
function: { name: tc.function.name, arguments: tc.function.arguments }
|
|
1337
|
+
}));
|
|
1338
|
+
if (message.content || toolCalls.length > 0) {
|
|
1339
|
+
return { content: message.content ?? null, tool_calls: toolCalls };
|
|
1249
1340
|
}
|
|
1250
1341
|
if (attempt === MAX_EMPTY_RETRIES) {
|
|
1251
1342
|
const finishReason = choice?.finish_reason ?? "unknown";
|
|
1252
|
-
const hasToolCalls = Array.isArray(choice?.message?.tool_calls) && choice.message.tool_calls.length > 0;
|
|
1253
|
-
const choicesLen = data?.choices?.length ?? 0;
|
|
1254
|
-
const contentType = content === null ? "null" : content === void 0 ? "undefined" : typeof content;
|
|
1255
1343
|
throw new Error(
|
|
1256
|
-
`Invalid response from model: content
|
|
1344
|
+
`Invalid response from model: no content and no tool_calls, finish_reason=${finishReason}`
|
|
1257
1345
|
);
|
|
1258
1346
|
}
|
|
1259
1347
|
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
1260
1348
|
}
|
|
1261
1349
|
throw new Error("Invalid response from model");
|
|
1262
1350
|
}
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
}).catch((e) => {
|
|
1290
|
-
const errMsg = e instanceof Error ? e.message : String(e);
|
|
1291
|
-
console.error(`[warp_grep] Morph API call failed on turn ${turn}:`, errMsg);
|
|
1292
|
-
errors.push({ message: errMsg });
|
|
1293
|
-
return "";
|
|
1294
|
-
});
|
|
1295
|
-
turnMetrics.morph_api_ms = Date.now() - modelCallStart;
|
|
1296
|
-
if (!assistantContent) {
|
|
1297
|
-
console.error(`[warp_grep] Empty response from Morph API on turn ${turn}. Errors so far:`, errors);
|
|
1298
|
-
timings.turns.push(turnMetrics);
|
|
1299
|
-
break;
|
|
1300
|
-
}
|
|
1301
|
-
messages.push({ role: "assistant", content: assistantContent });
|
|
1302
|
-
const toolCalls = parser.parse(assistantContent);
|
|
1303
|
-
if (toolCalls.length === 0) {
|
|
1304
|
-
console.error(`[warp_grep] No tool calls parsed on turn ${turn}. Assistant content (first 500 chars):`, assistantContent.slice(0, 500));
|
|
1305
|
-
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" });
|
|
1306
|
-
terminationReason = "terminated";
|
|
1307
|
-
timings.turns.push(turnMetrics);
|
|
1308
|
-
break;
|
|
1309
|
-
}
|
|
1310
|
-
const finishCalls = toolCalls.filter((c) => c.name === "finish");
|
|
1311
|
-
const grepCalls = toolCalls.filter((c) => c.name === "grep");
|
|
1312
|
-
const listDirCalls = toolCalls.filter((c) => c.name === "list_directory");
|
|
1313
|
-
const readCalls = toolCalls.filter((c) => c.name === "read");
|
|
1314
|
-
const skipCalls = toolCalls.filter((c) => c.name === "_skip");
|
|
1315
|
-
const formatted = [];
|
|
1316
|
-
for (const c of skipCalls) {
|
|
1317
|
-
const msg = c.arguments?.message || "Command skipped due to parsing error";
|
|
1318
|
-
formatted.push(msg);
|
|
1319
|
-
}
|
|
1320
|
-
const allPromises = [];
|
|
1321
|
-
for (const c of grepCalls) {
|
|
1322
|
-
const args = c.arguments ?? {};
|
|
1323
|
-
allPromises.push(
|
|
1324
|
-
toolGrep(provider, args).then(
|
|
1325
|
-
({ output }) => formatAgentToolOutput("grep", args, output),
|
|
1326
|
-
(err) => formatAgentToolOutput("grep", args, String(err), { isError: true })
|
|
1327
|
-
)
|
|
1328
|
-
);
|
|
1329
|
-
}
|
|
1330
|
-
for (const c of listDirCalls) {
|
|
1331
|
-
const args = c.arguments ?? {};
|
|
1332
|
-
allPromises.push(
|
|
1333
|
-
toolListDirectory(provider, args).then(
|
|
1334
|
-
(p) => formatAgentToolOutput("list_directory", args, p),
|
|
1335
|
-
(err) => formatAgentToolOutput("list_directory", args, String(err), { isError: true })
|
|
1336
|
-
)
|
|
1337
|
-
);
|
|
1338
|
-
}
|
|
1339
|
-
for (const c of readCalls) {
|
|
1340
|
-
const args = c.arguments ?? {};
|
|
1341
|
-
allPromises.push(
|
|
1342
|
-
toolRead(provider, args).then(
|
|
1343
|
-
(p) => formatAgentToolOutput("read", args, p),
|
|
1344
|
-
(err) => formatAgentToolOutput("read", args, String(err), { isError: true })
|
|
1345
|
-
)
|
|
1346
|
-
);
|
|
1347
|
-
}
|
|
1348
|
-
const toolExecStart = Date.now();
|
|
1349
|
-
const allResults = await Promise.all(allPromises);
|
|
1350
|
-
turnMetrics.local_tools_ms = Date.now() - toolExecStart;
|
|
1351
|
-
for (const result of allResults) {
|
|
1352
|
-
formatted.push(result);
|
|
1351
|
+
function safeParseJSON(s) {
|
|
1352
|
+
try {
|
|
1353
|
+
return JSON.parse(s);
|
|
1354
|
+
} catch {
|
|
1355
|
+
return {};
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
async function executeTool(provider, name, args, repoRoot) {
|
|
1359
|
+
switch (name) {
|
|
1360
|
+
case "grep_search": {
|
|
1361
|
+
const grepArgs = {
|
|
1362
|
+
pattern: args.pattern,
|
|
1363
|
+
path: args.path || "."
|
|
1364
|
+
};
|
|
1365
|
+
if (args.glob) grepArgs.glob = args.glob;
|
|
1366
|
+
if (args.case_sensitive !== void 0) grepArgs.case_sensitive = args.case_sensitive;
|
|
1367
|
+
const result = await toolGrep(provider, grepArgs);
|
|
1368
|
+
let output = result.output;
|
|
1369
|
+
if (args.limit && typeof args.limit === "number") {
|
|
1370
|
+
const lines = output.split("\n");
|
|
1371
|
+
if (lines.length > args.limit) {
|
|
1372
|
+
output = lines.slice(0, args.limit).join("\n") + `
|
|
1373
|
+
... (truncated at ${args.limit} lines)`;
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
return output;
|
|
1353
1377
|
}
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1378
|
+
case "glob": {
|
|
1379
|
+
return toolGlob(provider, {
|
|
1380
|
+
pattern: args.pattern,
|
|
1381
|
+
path: args.path
|
|
1382
|
+
});
|
|
1358
1383
|
}
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
const files = fc.arguments?.files ?? [];
|
|
1363
|
-
const textResult = fc.arguments?.textResult;
|
|
1364
|
-
finishMeta = { files };
|
|
1365
|
-
terminationReason = "completed";
|
|
1366
|
-
if (files.length === 0) {
|
|
1367
|
-
const payload2 = textResult || "No relevant code found.";
|
|
1368
|
-
timings.turns.push(turnMetrics);
|
|
1369
|
-
timings.total_ms = Date.now() - totalStart;
|
|
1370
|
-
return {
|
|
1371
|
-
terminationReason: "completed",
|
|
1372
|
-
messages,
|
|
1373
|
-
finish: { payload: payload2, metadata: finishMeta },
|
|
1374
|
-
timings
|
|
1375
|
-
};
|
|
1376
|
-
}
|
|
1377
|
-
break;
|
|
1384
|
+
case "list_directory": {
|
|
1385
|
+
const dirPath = extractPathFromCommand(args.command || ".");
|
|
1386
|
+
return toolListDirectory(provider, { path: dirPath }, repoRoot);
|
|
1378
1387
|
}
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
for (const f of finishMeta.files) {
|
|
1386
|
-
const ranges = f.lines === "*" ? "*" : Array.isArray(f.lines) ? f.lines.map(([s, e]) => `${s}-${e}`).join(", ") : "*";
|
|
1387
|
-
parts.push(`- ${f.path}: ${ranges}`);
|
|
1388
|
-
}
|
|
1389
|
-
const payload = parts.join("\n");
|
|
1390
|
-
const finishResolutionStart = Date.now();
|
|
1391
|
-
const fileReadErrors = [];
|
|
1392
|
-
const resolved = await readFinishFiles(
|
|
1393
|
-
repoRoot,
|
|
1394
|
-
finishMeta.files,
|
|
1395
|
-
async (p, s, e) => {
|
|
1396
|
-
try {
|
|
1397
|
-
const rr = await provider.read({ path: p, start: s, end: e });
|
|
1398
|
-
return rr.lines.map((l) => {
|
|
1399
|
-
const idx = l.indexOf("|");
|
|
1400
|
-
return idx >= 0 ? l.slice(idx + 1) : l;
|
|
1401
|
-
});
|
|
1402
|
-
} catch (err) {
|
|
1403
|
-
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
1404
|
-
fileReadErrors.push({ path: p, error: errorMsg });
|
|
1405
|
-
console.error(`[warp_grep] Failed to read file: ${p} - ${errorMsg}`);
|
|
1406
|
-
return [`[couldn't find: ${p}]`];
|
|
1388
|
+
case "read": {
|
|
1389
|
+
const readArgs = {
|
|
1390
|
+
path: args.path
|
|
1391
|
+
};
|
|
1392
|
+
if (args.lines && typeof args.lines === "string") {
|
|
1393
|
+
Object.assign(readArgs, parseReadLines(args.lines));
|
|
1407
1394
|
}
|
|
1395
|
+
return toolRead(provider, readArgs);
|
|
1408
1396
|
}
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
if (fileReadErrors.length > 0) {
|
|
1412
|
-
errors.push(...fileReadErrors.map((e) => ({ message: `File read error: ${e.path} - ${e.error}` })));
|
|
1397
|
+
default:
|
|
1398
|
+
return `Unknown tool: ${name}`;
|
|
1413
1399
|
}
|
|
1414
|
-
timings.total_ms = Date.now() - totalStart;
|
|
1415
|
-
return {
|
|
1416
|
-
terminationReason: "completed",
|
|
1417
|
-
messages,
|
|
1418
|
-
finish: { payload, metadata: finishMeta, resolved },
|
|
1419
|
-
timings
|
|
1420
|
-
};
|
|
1421
1400
|
}
|
|
1422
1401
|
async function* runWarpGrepStreaming(config) {
|
|
1423
1402
|
const totalStart = Date.now();
|
|
1424
1403
|
const timeoutMs = config.timeout ?? AGENT_CONFIG.TIMEOUT_MS;
|
|
1425
1404
|
const timings = { turns: [], timeout_ms: timeoutMs };
|
|
1426
|
-
const repoRoot =
|
|
1405
|
+
const repoRoot = import_path3.default.resolve(config.repoRoot || process.cwd());
|
|
1427
1406
|
const model = config.model || DEFAULT_MODEL;
|
|
1428
1407
|
const messages = [];
|
|
1429
1408
|
const maxTurns = AGENT_CONFIG.MAX_TURNS;
|
|
@@ -1439,7 +1418,7 @@ async function* runWarpGrepStreaming(config) {
|
|
|
1439
1418
|
const turnMetrics = { turn, morph_api_ms: 0, local_tools_ms: 0 };
|
|
1440
1419
|
enforceContextLimit(messages);
|
|
1441
1420
|
const modelCallStart = Date.now();
|
|
1442
|
-
const
|
|
1421
|
+
const response = await callModel(messages, model, {
|
|
1443
1422
|
morphApiKey: config.morphApiKey,
|
|
1444
1423
|
morphApiUrl: config.morphApiUrl,
|
|
1445
1424
|
retryConfig: config.retryConfig,
|
|
@@ -1447,90 +1426,45 @@ async function* runWarpGrepStreaming(config) {
|
|
|
1447
1426
|
search_type: config.search_type
|
|
1448
1427
|
}).catch((e) => {
|
|
1449
1428
|
const errMsg = e instanceof Error ? e.message : String(e);
|
|
1450
|
-
console.error(`[warp_grep
|
|
1429
|
+
console.error(`[warp_grep] Morph API call failed on turn ${turn}:`, errMsg);
|
|
1451
1430
|
errors.push({ message: errMsg });
|
|
1452
|
-
return
|
|
1431
|
+
return null;
|
|
1453
1432
|
});
|
|
1454
1433
|
turnMetrics.morph_api_ms = Date.now() - modelCallStart;
|
|
1455
|
-
if (!
|
|
1456
|
-
console.error(`[warp_grep:stream] Empty response from Morph API on turn ${turn}. Errors so far:`, errors);
|
|
1434
|
+
if (!response) {
|
|
1457
1435
|
timings.turns.push(turnMetrics);
|
|
1458
1436
|
break;
|
|
1459
1437
|
}
|
|
1460
|
-
|
|
1461
|
-
|
|
1438
|
+
const toolCalls = response.tool_calls;
|
|
1439
|
+
messages.push({
|
|
1440
|
+
role: "assistant",
|
|
1441
|
+
content: response.content,
|
|
1442
|
+
...toolCalls.length > 0 ? { tool_calls: toolCalls } : {}
|
|
1443
|
+
});
|
|
1462
1444
|
if (toolCalls.length === 0) {
|
|
1463
|
-
console.error(`[warp_grep
|
|
1464
|
-
errors.push({ message: "No tool calls produced by the model.
|
|
1445
|
+
console.error(`[warp_grep] No tool calls on turn ${turn}. Content: ${(response.content || "").slice(0, 500)}`);
|
|
1446
|
+
errors.push({ message: "No tool calls produced by the model." });
|
|
1465
1447
|
terminationReason = "terminated";
|
|
1466
1448
|
timings.turns.push(turnMetrics);
|
|
1467
1449
|
break;
|
|
1468
1450
|
}
|
|
1469
1451
|
yield {
|
|
1470
1452
|
turn,
|
|
1471
|
-
toolCalls: toolCalls.map((
|
|
1472
|
-
name:
|
|
1473
|
-
arguments:
|
|
1453
|
+
toolCalls: toolCalls.map((tc) => ({
|
|
1454
|
+
name: tc.function.name,
|
|
1455
|
+
arguments: safeParseJSON(tc.function.arguments)
|
|
1474
1456
|
}))
|
|
1475
1457
|
};
|
|
1476
|
-
const
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
const formatted = [];
|
|
1482
|
-
for (const c of skipCalls) {
|
|
1483
|
-
const msg = c.arguments?.message || "Command skipped due to parsing error";
|
|
1484
|
-
formatted.push(msg);
|
|
1485
|
-
}
|
|
1486
|
-
const allPromises = [];
|
|
1487
|
-
for (const c of grepCalls) {
|
|
1488
|
-
const args = c.arguments ?? {};
|
|
1489
|
-
allPromises.push(
|
|
1490
|
-
toolGrep(provider, args).then(
|
|
1491
|
-
({ output }) => formatAgentToolOutput("grep", args, output),
|
|
1492
|
-
(err) => formatAgentToolOutput("grep", args, String(err), { isError: true })
|
|
1493
|
-
)
|
|
1494
|
-
);
|
|
1495
|
-
}
|
|
1496
|
-
for (const c of listDirCalls) {
|
|
1497
|
-
const args = c.arguments ?? {};
|
|
1498
|
-
allPromises.push(
|
|
1499
|
-
toolListDirectory(provider, args).then(
|
|
1500
|
-
(p) => formatAgentToolOutput("list_directory", args, p),
|
|
1501
|
-
(err) => formatAgentToolOutput("list_directory", args, String(err), { isError: true })
|
|
1502
|
-
)
|
|
1503
|
-
);
|
|
1504
|
-
}
|
|
1505
|
-
for (const c of readCalls) {
|
|
1506
|
-
const args = c.arguments ?? {};
|
|
1507
|
-
allPromises.push(
|
|
1508
|
-
toolRead(provider, args).then(
|
|
1509
|
-
(p) => formatAgentToolOutput("read", args, p),
|
|
1510
|
-
(err) => formatAgentToolOutput("read", args, String(err), { isError: true })
|
|
1511
|
-
)
|
|
1512
|
-
);
|
|
1513
|
-
}
|
|
1514
|
-
const toolExecStart = Date.now();
|
|
1515
|
-
const allResults = await Promise.all(allPromises);
|
|
1516
|
-
turnMetrics.local_tools_ms = Date.now() - toolExecStart;
|
|
1517
|
-
for (const result of allResults) {
|
|
1518
|
-
formatted.push(result);
|
|
1519
|
-
}
|
|
1520
|
-
if (formatted.length > 0) {
|
|
1521
|
-
const turnMessage = formatTurnMessage(turn, maxTurns);
|
|
1522
|
-
const contextBudget = calculateContextBudget(messages);
|
|
1523
|
-
messages.push({ role: "user", content: formatted.join("\n") + turnMessage + "\n" + contextBudget });
|
|
1524
|
-
}
|
|
1525
|
-
timings.turns.push(turnMetrics);
|
|
1526
|
-
if (finishCalls.length) {
|
|
1527
|
-
const fc = finishCalls[0];
|
|
1528
|
-
const files = fc.arguments?.files ?? [];
|
|
1529
|
-
const textResult = fc.arguments?.textResult;
|
|
1458
|
+
const finishCall = toolCalls.find((tc) => tc.function.name === "finish");
|
|
1459
|
+
if (finishCall) {
|
|
1460
|
+
const args = safeParseJSON(finishCall.function.arguments);
|
|
1461
|
+
const filesStr = args.files || "";
|
|
1462
|
+
const files = parseFinishFiles(filesStr);
|
|
1530
1463
|
finishMeta = { files };
|
|
1531
1464
|
terminationReason = "completed";
|
|
1532
1465
|
if (files.length === 0) {
|
|
1533
|
-
const payload2 =
|
|
1466
|
+
const payload2 = filesStr || "No relevant code found.";
|
|
1467
|
+
timings.turns.push(turnMetrics);
|
|
1534
1468
|
timings.total_ms = Date.now() - totalStart;
|
|
1535
1469
|
return {
|
|
1536
1470
|
terminationReason: "completed",
|
|
@@ -1539,8 +1473,25 @@ async function* runWarpGrepStreaming(config) {
|
|
|
1539
1473
|
timings
|
|
1540
1474
|
};
|
|
1541
1475
|
}
|
|
1476
|
+
timings.turns.push(turnMetrics);
|
|
1542
1477
|
break;
|
|
1543
1478
|
}
|
|
1479
|
+
const toolExecStart = Date.now();
|
|
1480
|
+
const results = await Promise.all(
|
|
1481
|
+
toolCalls.map(async (tc) => {
|
|
1482
|
+
const args = safeParseJSON(tc.function.arguments);
|
|
1483
|
+
const output = await executeTool(provider, tc.function.name, args, repoRoot).catch((err) => String(err));
|
|
1484
|
+
return { tool_call_id: tc.id, content: output };
|
|
1485
|
+
})
|
|
1486
|
+
);
|
|
1487
|
+
turnMetrics.local_tools_ms = Date.now() - toolExecStart;
|
|
1488
|
+
for (const result of results) {
|
|
1489
|
+
messages.push({ role: "tool", tool_call_id: result.tool_call_id, content: result.content });
|
|
1490
|
+
}
|
|
1491
|
+
const turnMsg = formatTurnMessage(turn, maxTurns);
|
|
1492
|
+
const budget = calculateContextBudget(messages);
|
|
1493
|
+
messages.push({ role: "user", content: turnMsg + "\n" + budget });
|
|
1494
|
+
timings.turns.push(turnMetrics);
|
|
1544
1495
|
}
|
|
1545
1496
|
if (terminationReason !== "completed" || !finishMeta) {
|
|
1546
1497
|
timings.total_ms = Date.now() - totalStart;
|
|
@@ -1558,17 +1509,22 @@ async function* runWarpGrepStreaming(config) {
|
|
|
1558
1509
|
repoRoot,
|
|
1559
1510
|
finishMeta.files,
|
|
1560
1511
|
async (p, s, e) => {
|
|
1512
|
+
let resolvedPath = p;
|
|
1513
|
+
if (!p.startsWith(repoRoot)) {
|
|
1514
|
+
const relative = p.startsWith("/") ? p.slice(1) : p;
|
|
1515
|
+
resolvedPath = import_path3.default.join(repoRoot, relative);
|
|
1516
|
+
}
|
|
1561
1517
|
try {
|
|
1562
|
-
const rr = await provider.read({ path:
|
|
1518
|
+
const rr = await provider.read({ path: resolvedPath, start: s, end: e });
|
|
1563
1519
|
return rr.lines.map((l) => {
|
|
1564
1520
|
const idx = l.indexOf("|");
|
|
1565
1521
|
return idx >= 0 ? l.slice(idx + 1) : l;
|
|
1566
1522
|
});
|
|
1567
1523
|
} catch (err) {
|
|
1568
1524
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
1569
|
-
fileReadErrors.push({ path:
|
|
1570
|
-
console.error(`[warp_grep] Failed to read file: ${
|
|
1571
|
-
return [`[couldn't find: ${
|
|
1525
|
+
fileReadErrors.push({ path: resolvedPath, error: errorMsg });
|
|
1526
|
+
console.error(`[warp_grep] Failed to read file: ${resolvedPath} - ${errorMsg}`);
|
|
1527
|
+
return [`[couldn't find: ${resolvedPath}]`];
|
|
1572
1528
|
}
|
|
1573
1529
|
}
|
|
1574
1530
|
);
|
|
@@ -1584,6 +1540,14 @@ async function* runWarpGrepStreaming(config) {
|
|
|
1584
1540
|
timings
|
|
1585
1541
|
};
|
|
1586
1542
|
}
|
|
1543
|
+
async function runWarpGrep(config) {
|
|
1544
|
+
const gen = runWarpGrepStreaming(config);
|
|
1545
|
+
let result = await gen.next();
|
|
1546
|
+
while (!result.done) {
|
|
1547
|
+
result = await gen.next();
|
|
1548
|
+
}
|
|
1549
|
+
return result.value;
|
|
1550
|
+
}
|
|
1587
1551
|
|
|
1588
1552
|
// tools/warp_grep/providers/remote.ts
|
|
1589
1553
|
init_config();
|
|
@@ -1763,6 +1727,35 @@ var RemoteCommandsProvider = class {
|
|
|
1763
1727
|
return [];
|
|
1764
1728
|
}
|
|
1765
1729
|
}
|
|
1730
|
+
/**
|
|
1731
|
+
* Glob search - finds files matching a pattern.
|
|
1732
|
+
* Falls back to a grep --files approach via the listDir command.
|
|
1733
|
+
*/
|
|
1734
|
+
async glob(params) {
|
|
1735
|
+
const searchPath = params.path || this.repoRoot;
|
|
1736
|
+
try {
|
|
1737
|
+
const stdout = await this.commands.listDir(searchPath, 10);
|
|
1738
|
+
const allPaths = (stdout || "").trim().split(/\r?\n/).filter((p) => p.length > 0);
|
|
1739
|
+
const globToRegex = (glob) => {
|
|
1740
|
+
const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
1741
|
+
return new RegExp(escaped);
|
|
1742
|
+
};
|
|
1743
|
+
const regex = globToRegex(params.pattern);
|
|
1744
|
+
const matched = allPaths.filter((p) => {
|
|
1745
|
+
const name = p.split("/").pop() || "";
|
|
1746
|
+
return regex.test(name) && !shouldSkip(name);
|
|
1747
|
+
});
|
|
1748
|
+
const totalFound = matched.length;
|
|
1749
|
+
return { files: matched.slice(0, 100), searchDir: searchPath, totalFound };
|
|
1750
|
+
} catch (error) {
|
|
1751
|
+
return {
|
|
1752
|
+
files: [],
|
|
1753
|
+
searchDir: searchPath,
|
|
1754
|
+
totalFound: 0,
|
|
1755
|
+
error: `[GLOB ERROR] ${error instanceof Error ? error.message : String(error)}`
|
|
1756
|
+
};
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1766
1759
|
};
|
|
1767
1760
|
|
|
1768
1761
|
// tools/warp_grep/providers/code_storage_http.ts
|
|
@@ -1789,9 +1782,9 @@ function createCodeStorageHttpCommands(config) {
|
|
|
1789
1782
|
const { baseUrl, repoId, branch } = config;
|
|
1790
1783
|
const encodedRepoId = encodeURIComponent(repoId);
|
|
1791
1784
|
return {
|
|
1792
|
-
grep: (pattern,
|
|
1793
|
-
read: (
|
|
1794
|
-
listDir: (
|
|
1785
|
+
grep: (pattern, path6, glob) => post(`${baseUrl}/api/code-search/${encodedRepoId}/grep`, { pattern, path: path6, glob, branch }, "grep"),
|
|
1786
|
+
read: (path6, start, end) => post(`${baseUrl}/api/code-search/${encodedRepoId}/read`, { path: path6, start, end, branch }, "read"),
|
|
1787
|
+
listDir: (path6, maxDepth) => post(`${baseUrl}/api/code-search/${encodedRepoId}/list`, { path: path6, maxDepth, branch }, "list")
|
|
1795
1788
|
};
|
|
1796
1789
|
}
|
|
1797
1790
|
|