@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
package/dist/client.cjs
CHANGED
|
@@ -36,7 +36,7 @@ var init_package = __esm({
|
|
|
36
36
|
"package.json"() {
|
|
37
37
|
package_default = {
|
|
38
38
|
name: "@morphllm/morphsdk",
|
|
39
|
-
version: "0.2.
|
|
39
|
+
version: "0.2.148",
|
|
40
40
|
description: "TypeScript SDK and CLI for Morph Fast Apply integration",
|
|
41
41
|
type: "module",
|
|
42
42
|
main: "./dist/index.cjs",
|
|
@@ -416,11 +416,12 @@ var init_config = __esm({
|
|
|
416
416
|
return isNaN(parsed) || parsed <= 0 ? defaultMs : parsed;
|
|
417
417
|
};
|
|
418
418
|
AGENT_CONFIG = {
|
|
419
|
-
MAX_TURNS:
|
|
419
|
+
MAX_TURNS: 6,
|
|
420
420
|
/** Default timeout for model calls. Can be overridden via MORPH_WARP_GREP_TIMEOUT env var (in ms) */
|
|
421
421
|
TIMEOUT_MS: parseEnvTimeout(process.env.MORPH_WARP_GREP_TIMEOUT, 3e4),
|
|
422
|
-
MAX_CONTEXT_CHARS:
|
|
422
|
+
MAX_CONTEXT_CHARS: 321600,
|
|
423
423
|
MAX_OUTPUT_LINES: 200,
|
|
424
|
+
MAX_LIST_RESULTS: 500,
|
|
424
425
|
MAX_READ_LINES: 800,
|
|
425
426
|
MAX_LIST_DEPTH: 3,
|
|
426
427
|
LIST_TIMEOUT_MS: 2e3
|
|
@@ -505,7 +506,7 @@ var init_config = __esm({
|
|
|
505
506
|
".*"
|
|
506
507
|
];
|
|
507
508
|
DEFAULT_EXCLUDES = (process.env.MORPH_WARP_GREP_EXCLUDE || "").split(",").map((s) => s.trim()).filter(Boolean).concat(BUILTIN_EXCLUDES);
|
|
508
|
-
DEFAULT_MODEL = "morph-warp-grep-v2";
|
|
509
|
+
DEFAULT_MODEL = "morph-warp-grep-v2.1";
|
|
509
510
|
}
|
|
510
511
|
});
|
|
511
512
|
|
|
@@ -578,19 +579,19 @@ var init_ripgrep = __esm({
|
|
|
578
579
|
|
|
579
580
|
// tools/warp_grep/utils/paths.ts
|
|
580
581
|
function resolveUnderRepo(repoRoot, targetPath) {
|
|
581
|
-
const absRoot =
|
|
582
|
-
const resolved =
|
|
582
|
+
const absRoot = import_path5.default.resolve(repoRoot);
|
|
583
|
+
const resolved = import_path5.default.resolve(absRoot, targetPath);
|
|
583
584
|
ensureWithinRepo(absRoot, resolved);
|
|
584
585
|
return resolved;
|
|
585
586
|
}
|
|
586
587
|
function ensureWithinRepo(repoRoot, absTarget) {
|
|
587
|
-
const rel =
|
|
588
|
-
if (rel.startsWith("..") ||
|
|
588
|
+
const rel = import_path5.default.relative(import_path5.default.resolve(repoRoot), import_path5.default.resolve(absTarget));
|
|
589
|
+
if (rel.startsWith("..") || import_path5.default.isAbsolute(rel)) {
|
|
589
590
|
throw new Error(`Path outside repository root: ${absTarget}`);
|
|
590
591
|
}
|
|
591
592
|
}
|
|
592
593
|
function toRepoRelative(repoRoot, absPath) {
|
|
593
|
-
return
|
|
594
|
+
return import_path5.default.relative(import_path5.default.resolve(repoRoot), import_path5.default.resolve(absPath));
|
|
594
595
|
}
|
|
595
596
|
function isSymlink(p) {
|
|
596
597
|
try {
|
|
@@ -601,7 +602,7 @@ function isSymlink(p) {
|
|
|
601
602
|
}
|
|
602
603
|
}
|
|
603
604
|
function fixPathRepetition(fullPath) {
|
|
604
|
-
const segments = fullPath.split(
|
|
605
|
+
const segments = fullPath.split(import_path5.default.sep).filter(Boolean);
|
|
605
606
|
if (segments.length < 2) return null;
|
|
606
607
|
for (let len = Math.floor(segments.length / 2); len >= 1; len--) {
|
|
607
608
|
for (let i = 0; i <= segments.length - 2 * len; i++) {
|
|
@@ -609,7 +610,7 @@ function fixPathRepetition(fullPath) {
|
|
|
609
610
|
const second = segments.slice(i + len, i + 2 * len);
|
|
610
611
|
if (first.every((seg, idx) => seg === second[idx])) {
|
|
611
612
|
const fixed = [...segments.slice(0, i), ...segments.slice(i + len)];
|
|
612
|
-
return
|
|
613
|
+
return import_path5.default.sep + fixed.join(import_path5.default.sep);
|
|
613
614
|
}
|
|
614
615
|
}
|
|
615
616
|
}
|
|
@@ -633,12 +634,12 @@ function isTextualFile(filePath, maxBytes = 2e6) {
|
|
|
633
634
|
return false;
|
|
634
635
|
}
|
|
635
636
|
}
|
|
636
|
-
var import_fs,
|
|
637
|
+
var import_fs, import_path5;
|
|
637
638
|
var init_paths = __esm({
|
|
638
639
|
"tools/warp_grep/utils/paths.ts"() {
|
|
639
640
|
"use strict";
|
|
640
641
|
import_fs = __toESM(require("fs"), 1);
|
|
641
|
-
|
|
642
|
+
import_path5 = __toESM(require("path"), 1);
|
|
642
643
|
}
|
|
643
644
|
});
|
|
644
645
|
|
|
@@ -669,12 +670,12 @@ function shouldSkip2(name, allowNames) {
|
|
|
669
670
|
}
|
|
670
671
|
return false;
|
|
671
672
|
}
|
|
672
|
-
var import_promises2,
|
|
673
|
+
var import_promises2, import_path6, SKIP_NAMES2, SKIP_EXTENSIONS2, LocalRipgrepProvider;
|
|
673
674
|
var init_local = __esm({
|
|
674
675
|
"tools/warp_grep/providers/local.ts"() {
|
|
675
676
|
"use strict";
|
|
676
677
|
import_promises2 = __toESM(require("fs/promises"), 1);
|
|
677
|
-
|
|
678
|
+
import_path6 = __toESM(require("path"), 1);
|
|
678
679
|
init_ripgrep();
|
|
679
680
|
init_paths();
|
|
680
681
|
init_files();
|
|
@@ -776,7 +777,7 @@ var init_local = __esm({
|
|
|
776
777
|
}
|
|
777
778
|
const stat = await import_promises2.default.stat(abs).catch(() => null);
|
|
778
779
|
if (!stat) return { lines: [] };
|
|
779
|
-
const targetArg = abs ===
|
|
780
|
+
const targetArg = abs === import_path6.default.resolve(this.repoRoot) ? "." : toRepoRelative(this.repoRoot, abs);
|
|
780
781
|
const contextLines = params.context_lines !== void 0 ? String(params.context_lines) : "1";
|
|
781
782
|
const args = [
|
|
782
783
|
"--no-config",
|
|
@@ -931,7 +932,7 @@ Details: ${res.stderr}` : ""}`
|
|
|
931
932
|
if (timedOut || results.length >= maxResults) break;
|
|
932
933
|
if (shouldSkip2(entry.name, allowNames)) continue;
|
|
933
934
|
if (regex && !regex.test(entry.name)) continue;
|
|
934
|
-
const full =
|
|
935
|
+
const full = import_path6.default.join(dir, entry.name);
|
|
935
936
|
const isDir = entry.isDirectory();
|
|
936
937
|
results.push({
|
|
937
938
|
name: entry.name,
|
|
@@ -947,6 +948,46 @@ Details: ${res.stderr}` : ""}`
|
|
|
947
948
|
await walk(abs, 0);
|
|
948
949
|
return results;
|
|
949
950
|
}
|
|
951
|
+
async glob(params) {
|
|
952
|
+
let abs;
|
|
953
|
+
try {
|
|
954
|
+
abs = params.path ? resolveUnderRepo(this.repoRoot, params.path) : this.repoRoot;
|
|
955
|
+
} catch (err) {
|
|
956
|
+
return { files: [], searchDir: this.repoRoot, totalFound: 0, error: `[PATH ERROR] ${err instanceof Error ? err.message : String(err)}` };
|
|
957
|
+
}
|
|
958
|
+
const stat = await import_promises2.default.stat(abs).catch(() => null);
|
|
959
|
+
if (!stat || !stat.isDirectory()) {
|
|
960
|
+
return { files: [], searchDir: abs, totalFound: 0, error: `[PATH ERROR] Directory not found: ${params.path || "."}` };
|
|
961
|
+
}
|
|
962
|
+
const targetArg = abs === import_path6.default.resolve(this.repoRoot) ? "." : toRepoRelative(this.repoRoot, abs);
|
|
963
|
+
const args = [
|
|
964
|
+
"--no-config",
|
|
965
|
+
"--files",
|
|
966
|
+
"--color=never",
|
|
967
|
+
"-g",
|
|
968
|
+
params.pattern,
|
|
969
|
+
...this.excludes.filter((e) => !this.allowNames?.has(e)).flatMap((e) => ["-g", `!${e}`]),
|
|
970
|
+
targetArg || "."
|
|
971
|
+
];
|
|
972
|
+
const res = await runRipgrep(args, { cwd: this.repoRoot });
|
|
973
|
+
if (res.exitCode === -1) {
|
|
974
|
+
return { files: [], searchDir: abs, totalFound: 0, error: `[RIPGREP NOT AVAILABLE] ripgrep (rg) is required for glob search.` };
|
|
975
|
+
}
|
|
976
|
+
if (res.exitCode !== 0 && res.exitCode !== 1) {
|
|
977
|
+
return { files: [], searchDir: abs, totalFound: 0, error: `[GLOB ERROR] glob failed with exit code ${res.exitCode}${res.stderr ? `: ${res.stderr}` : ""}` };
|
|
978
|
+
}
|
|
979
|
+
const absRoot = import_path6.default.resolve(this.repoRoot);
|
|
980
|
+
const relFiles = (res.stdout || "").trim().split(/\r?\n/).filter((l) => l.length > 0);
|
|
981
|
+
const absFiles = relFiles.map((f) => import_path6.default.resolve(absRoot, f));
|
|
982
|
+
const withMtime = [];
|
|
983
|
+
for (const f of absFiles) {
|
|
984
|
+
const s = await import_promises2.default.stat(f).catch(() => null);
|
|
985
|
+
withMtime.push({ file: f, mtime: s?.mtimeMs ?? 0 });
|
|
986
|
+
}
|
|
987
|
+
withMtime.sort((a, b) => b.mtime - a.mtime);
|
|
988
|
+
const totalFound = withMtime.length;
|
|
989
|
+
return { files: withMtime.slice(0, 100).map((f) => f.file), searchDir: abs, totalFound };
|
|
990
|
+
}
|
|
950
991
|
};
|
|
951
992
|
}
|
|
952
993
|
});
|
|
@@ -2493,131 +2534,58 @@ async function checkHealth(config = {}) {
|
|
|
2493
2534
|
init_config();
|
|
2494
2535
|
|
|
2495
2536
|
// tools/warp_grep/agent/parser.ts
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2537
|
+
function parseReadLines(linesStr) {
|
|
2538
|
+
const ranges = [];
|
|
2539
|
+
for (const rangeStr of linesStr.split(",")) {
|
|
2540
|
+
const trimmed = rangeStr.trim();
|
|
2541
|
+
if (!trimmed) continue;
|
|
2542
|
+
const parts = trimmed.split("-").map((v) => parseInt(v.trim(), 10));
|
|
2543
|
+
if (parts.length >= 2 && Number.isFinite(parts[0]) && Number.isFinite(parts[1])) {
|
|
2544
|
+
ranges.push([parts[0], parts[1]]);
|
|
2545
|
+
} else if (Number.isFinite(parts[0])) {
|
|
2546
|
+
ranges.push([parts[0], parts[0]]);
|
|
2547
|
+
}
|
|
2548
|
+
}
|
|
2549
|
+
if (ranges.length === 1) return { start: ranges[0][0], end: ranges[0][1] };
|
|
2550
|
+
if (ranges.length > 1) return { lines: ranges };
|
|
2551
|
+
return {};
|
|
2499
2552
|
}
|
|
2500
|
-
function
|
|
2501
|
-
const
|
|
2502
|
-
const
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
const
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
}
|
|
2524
|
-
|
|
2525
|
-
} else if (funcName === "list_directory") {
|
|
2526
|
-
const command = params.command;
|
|
2527
|
-
const directPath = params.path;
|
|
2528
|
-
let dirPath = directPath || ".";
|
|
2529
|
-
if (!directPath && command) {
|
|
2530
|
-
const tokens = command.trim().split(/\s+/);
|
|
2531
|
-
const pathTokens = tokens.slice(1).filter((t) => !t.startsWith("-") && !t.startsWith("|") && !t.startsWith("\\("));
|
|
2532
|
-
if (pathTokens.length > 0) {
|
|
2533
|
-
dirPath = pathTokens[0];
|
|
2534
|
-
}
|
|
2535
|
-
}
|
|
2536
|
-
tools.push({ name: "list_directory", arguments: { path: dirPath, pattern: params.pattern || null } });
|
|
2537
|
-
} else if (funcName === "read") {
|
|
2538
|
-
const filePath = params.path;
|
|
2539
|
-
if (!filePath) continue;
|
|
2540
|
-
const args = { path: filePath };
|
|
2541
|
-
const linesStr = params.lines;
|
|
2542
|
-
if (linesStr) {
|
|
2543
|
-
const ranges = [];
|
|
2544
|
-
for (const rangeStr of linesStr.split(",")) {
|
|
2545
|
-
const trimmed = rangeStr.trim();
|
|
2546
|
-
if (!trimmed) continue;
|
|
2547
|
-
const [s, e] = trimmed.split("-").map((v) => parseInt(v.trim(), 10));
|
|
2548
|
-
if (Number.isFinite(s) && Number.isFinite(e)) {
|
|
2549
|
-
ranges.push([s, e]);
|
|
2550
|
-
} else if (Number.isFinite(s)) {
|
|
2551
|
-
ranges.push([s, s]);
|
|
2552
|
-
}
|
|
2553
|
-
}
|
|
2554
|
-
if (ranges.length === 1) {
|
|
2555
|
-
args.start = ranges[0][0];
|
|
2556
|
-
args.end = ranges[0][1];
|
|
2557
|
-
} else if (ranges.length > 1) {
|
|
2558
|
-
args.lines = ranges;
|
|
2559
|
-
}
|
|
2560
|
-
}
|
|
2561
|
-
tools.push({ name: "read", arguments: args });
|
|
2562
|
-
} else if (funcName === "finish") {
|
|
2563
|
-
if (params.result && !params.files) {
|
|
2564
|
-
tools.push({ name: "finish", arguments: { files: [], textResult: params.result } });
|
|
2565
|
-
continue;
|
|
2566
|
-
}
|
|
2567
|
-
const filesStr = params.files;
|
|
2568
|
-
if (!filesStr) {
|
|
2569
|
-
tools.push({ name: "finish", arguments: { files: [], textResult: "No relevant code found." } });
|
|
2570
|
-
continue;
|
|
2571
|
-
}
|
|
2572
|
-
const files = [];
|
|
2573
|
-
for (const line of filesStr.split("\n")) {
|
|
2574
|
-
const trimmed = line.trim();
|
|
2575
|
-
if (!trimmed) continue;
|
|
2576
|
-
const colonIdx = trimmed.indexOf(":");
|
|
2577
|
-
if (colonIdx === -1) {
|
|
2578
|
-
files.push({ path: trimmed, lines: "*" });
|
|
2579
|
-
} else {
|
|
2580
|
-
const filePath = trimmed.slice(0, colonIdx);
|
|
2581
|
-
const rangesPart = trimmed.slice(colonIdx + 1);
|
|
2582
|
-
const ranges = [];
|
|
2583
|
-
for (const rangeStr of rangesPart.split(",")) {
|
|
2584
|
-
const rt = rangeStr.trim();
|
|
2585
|
-
if (!rt || rt === "*") {
|
|
2586
|
-
files.push({ path: filePath, lines: "*" });
|
|
2587
|
-
break;
|
|
2588
|
-
}
|
|
2589
|
-
const [s, e] = rt.split("-").map((v) => parseInt(v.trim(), 10));
|
|
2590
|
-
if (Number.isFinite(s) && Number.isFinite(e)) {
|
|
2591
|
-
ranges.push([s, e]);
|
|
2592
|
-
} else if (Number.isFinite(s)) {
|
|
2593
|
-
ranges.push([s, s]);
|
|
2594
|
-
}
|
|
2595
|
-
}
|
|
2596
|
-
if (ranges.length > 0) {
|
|
2597
|
-
files.push({ path: filePath, lines: ranges });
|
|
2598
|
-
} else if (!files.some((f) => f.path === filePath)) {
|
|
2599
|
-
files.push({ path: filePath, lines: "*" });
|
|
2600
|
-
}
|
|
2601
|
-
}
|
|
2602
|
-
}
|
|
2603
|
-
if (files.length > 0) {
|
|
2604
|
-
tools.push({ name: "finish", arguments: { files } });
|
|
2605
|
-
} else {
|
|
2606
|
-
tools.push({ name: "finish", arguments: { files: [], textResult: filesStr } });
|
|
2553
|
+
function parseFinishFiles(filesStr) {
|
|
2554
|
+
const files = [];
|
|
2555
|
+
for (const line of filesStr.trim().split(/\s+/)) {
|
|
2556
|
+
const trimmed = line.trim();
|
|
2557
|
+
if (!trimmed) continue;
|
|
2558
|
+
const colonIdx = trimmed.indexOf(":");
|
|
2559
|
+
if (colonIdx === -1) {
|
|
2560
|
+
files.push({ path: trimmed, lines: "*" });
|
|
2561
|
+
continue;
|
|
2562
|
+
}
|
|
2563
|
+
const filePath = trimmed.slice(0, colonIdx);
|
|
2564
|
+
const rangesPart = trimmed.slice(colonIdx + 1);
|
|
2565
|
+
if (!rangesPart.trim() || rangesPart.trim() === "*") {
|
|
2566
|
+
files.push({ path: filePath, lines: "*" });
|
|
2567
|
+
continue;
|
|
2568
|
+
}
|
|
2569
|
+
const ranges = [];
|
|
2570
|
+
for (const rangeStr of rangesPart.split(",")) {
|
|
2571
|
+
const rt = rangeStr.trim();
|
|
2572
|
+
if (!rt) continue;
|
|
2573
|
+
const parts = rt.split("-").map((v) => parseInt(v.trim(), 10));
|
|
2574
|
+
if (parts.length >= 2 && Number.isFinite(parts[0]) && Number.isFinite(parts[1])) {
|
|
2575
|
+
ranges.push([parts[0], parts[1]]);
|
|
2576
|
+
} else if (Number.isFinite(parts[0])) {
|
|
2577
|
+
ranges.push([parts[0], parts[0]]);
|
|
2607
2578
|
}
|
|
2608
2579
|
}
|
|
2580
|
+
files.push({ path: filePath, lines: ranges.length > 0 ? ranges : "*" });
|
|
2609
2581
|
}
|
|
2610
|
-
return
|
|
2582
|
+
return files;
|
|
2583
|
+
}
|
|
2584
|
+
function extractPathFromCommand(command) {
|
|
2585
|
+
const tokens = command.trim().split(/\s+/);
|
|
2586
|
+
const pathTokens = tokens.slice(1).filter((t) => !t.startsWith("-") && !t.startsWith("|") && !t.startsWith("\\("));
|
|
2587
|
+
return pathTokens[0] || ".";
|
|
2611
2588
|
}
|
|
2612
|
-
var LLMResponseParser = class {
|
|
2613
|
-
parse(text) {
|
|
2614
|
-
if (typeof text !== "string") {
|
|
2615
|
-
throw new TypeError("Command text must be a string.");
|
|
2616
|
-
}
|
|
2617
|
-
const withoutThink = text.replace(/<think>[\s\S]*?<\/think>/gi, "");
|
|
2618
|
-
return parseQwen3ToolCalls(withoutThink);
|
|
2619
|
-
}
|
|
2620
|
-
};
|
|
2621
2589
|
|
|
2622
2590
|
// tools/warp_grep/agent/tools/grep.ts
|
|
2623
2591
|
async function toolGrep(provider, args) {
|
|
@@ -2668,29 +2636,42 @@ async function toolRead(provider, args) {
|
|
|
2668
2636
|
|
|
2669
2637
|
// tools/warp_grep/agent/tools/list_directory.ts
|
|
2670
2638
|
init_config();
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
const
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2639
|
+
var import_path2 = __toESM(require("path"), 1);
|
|
2640
|
+
async function toolListDirectory(provider, args, repoRoot) {
|
|
2641
|
+
const maxResults = args.maxResults ?? AGENT_CONFIG.MAX_LIST_RESULTS;
|
|
2642
|
+
const maxDepth = args.maxDepth ?? AGENT_CONFIG.MAX_LIST_DEPTH;
|
|
2643
|
+
const entries = await provider.listDirectory({
|
|
2644
|
+
path: args.path,
|
|
2645
|
+
pattern: args.pattern ?? null,
|
|
2646
|
+
maxResults,
|
|
2647
|
+
maxDepth
|
|
2648
|
+
});
|
|
2649
|
+
if (!entries.length) return "empty";
|
|
2650
|
+
if (repoRoot) {
|
|
2651
|
+
const absRoot = import_path2.default.resolve(repoRoot);
|
|
2652
|
+
const lines = entries.map((e) => import_path2.default.join(absRoot, e.path));
|
|
2653
|
+
return lines.join("\n");
|
|
2654
|
+
}
|
|
2655
|
+
return entries.map((e) => e.path).join("\n");
|
|
2656
|
+
}
|
|
2657
|
+
|
|
2658
|
+
// tools/warp_grep/agent/tools/glob.ts
|
|
2659
|
+
async function toolGlob(provider, args) {
|
|
2660
|
+
const res = await provider.glob(args);
|
|
2661
|
+
if (res.error) {
|
|
2662
|
+
return res.error;
|
|
2663
|
+
}
|
|
2664
|
+
if (!res.files.length) {
|
|
2665
|
+
return "no matches";
|
|
2666
|
+
}
|
|
2667
|
+
const header = `Found ${res.totalFound} file(s) matching "${args.pattern}" within ${res.searchDir}, sorted by modification time (newest first):`;
|
|
2668
|
+
const body = res.files.join("\n");
|
|
2669
|
+
const truncated = res.totalFound > res.files.length ? `
|
|
2670
|
+
[${res.totalFound - res.files.length} files truncated]` : "";
|
|
2671
|
+
return `${header}
|
|
2672
|
+
---
|
|
2673
|
+
${body}
|
|
2674
|
+
---${truncated}`;
|
|
2694
2675
|
}
|
|
2695
2676
|
|
|
2696
2677
|
// tools/warp_grep/agent/tools/finish.ts
|
|
@@ -2751,32 +2732,21 @@ function mergeRanges(ranges) {
|
|
|
2751
2732
|
return merged;
|
|
2752
2733
|
}
|
|
2753
2734
|
|
|
2754
|
-
// tools/warp_grep/agent/formatter.ts
|
|
2755
|
-
var ToolOutputFormatter = class {
|
|
2756
|
-
format(toolName, _args, output, options = {}) {
|
|
2757
|
-
const name = (toolName ?? "").trim();
|
|
2758
|
-
if (!name) {
|
|
2759
|
-
return "";
|
|
2760
|
-
}
|
|
2761
|
-
const payload = output?.toString?.()?.trim?.() ?? "";
|
|
2762
|
-
const isError = Boolean(options.isError);
|
|
2763
|
-
if (!payload && !isError) {
|
|
2764
|
-
return "";
|
|
2765
|
-
}
|
|
2766
|
-
return `<tool_response>
|
|
2767
|
-
${payload}
|
|
2768
|
-
</tool_response>`;
|
|
2769
|
-
}
|
|
2770
|
-
};
|
|
2771
|
-
var sharedFormatter = new ToolOutputFormatter();
|
|
2772
|
-
function formatAgentToolOutput(toolName, args, output, options = {}) {
|
|
2773
|
-
return sharedFormatter.format(toolName, args, output, options);
|
|
2774
|
-
}
|
|
2775
|
-
|
|
2776
2735
|
// tools/warp_grep/agent/helpers.ts
|
|
2777
|
-
var
|
|
2736
|
+
var import_path3 = __toESM(require("path"), 1);
|
|
2778
2737
|
init_config();
|
|
2779
2738
|
var TRUNCATED_MARKER = "[truncated for context limit]";
|
|
2739
|
+
function getMessageSize(m) {
|
|
2740
|
+
if (m.role === "tool") return m.content.length;
|
|
2741
|
+
if (m.role === "assistant") {
|
|
2742
|
+
let size = typeof m.content === "string" ? m.content.length : 0;
|
|
2743
|
+
if (m.tool_calls) {
|
|
2744
|
+
size += m.tool_calls.reduce((s, tc) => s + tc.function.name.length + tc.function.arguments.length, 0);
|
|
2745
|
+
}
|
|
2746
|
+
return size;
|
|
2747
|
+
}
|
|
2748
|
+
return m.content.length;
|
|
2749
|
+
}
|
|
2780
2750
|
function formatTurnMessage(turnsUsed, maxTurns) {
|
|
2781
2751
|
const turnsRemaining = maxTurns - turnsUsed;
|
|
2782
2752
|
if (turnsRemaining === 1) {
|
|
@@ -2787,33 +2757,30 @@ You have used ${turnsUsed} turns, you only have 1 turn remaining. You have run o
|
|
|
2787
2757
|
You have used ${turnsUsed} turn${turnsUsed === 1 ? "" : "s"} and have ${turnsRemaining} remaining`;
|
|
2788
2758
|
}
|
|
2789
2759
|
function calculateContextBudget(messages) {
|
|
2790
|
-
const totalChars = messages.reduce((sum, m) => sum + m
|
|
2760
|
+
const totalChars = messages.reduce((sum, m) => sum + getMessageSize(m), 0);
|
|
2791
2761
|
const maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS;
|
|
2792
|
-
const percent = Math.
|
|
2793
|
-
const usedK = Math.
|
|
2794
|
-
const maxK = Math.
|
|
2795
|
-
return `<context_budget>${percent}% (${usedK}K/${maxK}K chars
|
|
2762
|
+
const percent = Math.floor(totalChars / maxChars * 100);
|
|
2763
|
+
const usedK = Math.floor(totalChars / 1e3);
|
|
2764
|
+
const maxK = Math.floor(maxChars / 1e3);
|
|
2765
|
+
return `<context_budget>${percent}% (${usedK}K/${maxK}K chars)</context_budget>`;
|
|
2796
2766
|
}
|
|
2797
2767
|
async function buildInitialState(repoRoot, searchTerm, provider, options) {
|
|
2798
2768
|
const budget = calculateContextBudget([]);
|
|
2799
|
-
const turnTag = `
|
|
2769
|
+
const turnTag = `You have used 0 turns and have ${AGENT_CONFIG.MAX_TURNS} remaining`;
|
|
2800
2770
|
const treeDepth = options?.search_type === "node_modules" ? 1 : 2;
|
|
2771
|
+
const absRoot = import_path3.default.resolve(repoRoot);
|
|
2801
2772
|
try {
|
|
2802
2773
|
const entries = await provider.listDirectory({
|
|
2803
2774
|
path: ".",
|
|
2804
2775
|
maxResults: AGENT_CONFIG.MAX_OUTPUT_LINES,
|
|
2805
2776
|
maxDepth: treeDepth
|
|
2806
2777
|
});
|
|
2807
|
-
const
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
});
|
|
2812
|
-
const repoName = import_path2.default.basename(repoRoot);
|
|
2813
|
-
const treeOutput = treeLines.length > 0 ? `${repoName}/
|
|
2814
|
-
${treeLines.join("\n")}` : `${repoName}/`;
|
|
2778
|
+
const lines = [absRoot];
|
|
2779
|
+
for (const e of entries) {
|
|
2780
|
+
lines.push(import_path3.default.join(absRoot, e.path));
|
|
2781
|
+
}
|
|
2815
2782
|
return `<repo_structure>
|
|
2816
|
-
${
|
|
2783
|
+
${lines.join("\n")}
|
|
2817
2784
|
</repo_structure>
|
|
2818
2785
|
|
|
2819
2786
|
<search_string>
|
|
@@ -2822,9 +2789,8 @@ ${searchTerm}
|
|
|
2822
2789
|
${budget}
|
|
2823
2790
|
${turnTag}`;
|
|
2824
2791
|
} catch {
|
|
2825
|
-
const repoName = import_path2.default.basename(repoRoot);
|
|
2826
2792
|
return `<repo_structure>
|
|
2827
|
-
${
|
|
2793
|
+
${absRoot}
|
|
2828
2794
|
</repo_structure>
|
|
2829
2795
|
|
|
2830
2796
|
<search_string>
|
|
@@ -2835,26 +2801,32 @@ ${turnTag}`;
|
|
|
2835
2801
|
}
|
|
2836
2802
|
}
|
|
2837
2803
|
function enforceContextLimit(messages, maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS) {
|
|
2838
|
-
const getTotalChars = () => messages.reduce((sum, m) => sum + m
|
|
2804
|
+
const getTotalChars = () => messages.reduce((sum, m) => sum + getMessageSize(m), 0);
|
|
2839
2805
|
if (getTotalChars() <= maxChars) {
|
|
2840
2806
|
return messages;
|
|
2841
2807
|
}
|
|
2842
|
-
const
|
|
2808
|
+
const truncatableIndices = [];
|
|
2843
2809
|
let firstUserSkipped = false;
|
|
2844
2810
|
for (let i = 0; i < messages.length; i++) {
|
|
2845
|
-
|
|
2811
|
+
const m = messages[i];
|
|
2812
|
+
if (m.role === "tool") {
|
|
2813
|
+
truncatableIndices.push(i);
|
|
2814
|
+
} else if (m.role === "user") {
|
|
2846
2815
|
if (!firstUserSkipped) {
|
|
2847
2816
|
firstUserSkipped = true;
|
|
2848
2817
|
continue;
|
|
2849
2818
|
}
|
|
2850
|
-
|
|
2819
|
+
truncatableIndices.push(i);
|
|
2851
2820
|
}
|
|
2852
2821
|
}
|
|
2853
|
-
for (const idx of
|
|
2822
|
+
for (const idx of truncatableIndices) {
|
|
2854
2823
|
if (getTotalChars() <= maxChars) {
|
|
2855
2824
|
break;
|
|
2856
2825
|
}
|
|
2857
|
-
|
|
2826
|
+
const m = messages[idx];
|
|
2827
|
+
if (m.role === "tool" && m.content !== TRUNCATED_MARKER) {
|
|
2828
|
+
messages[idx] = { role: "tool", tool_call_id: m.tool_call_id, content: TRUNCATED_MARKER };
|
|
2829
|
+
} else if (m.role === "user" && m.content !== TRUNCATED_MARKER) {
|
|
2858
2830
|
messages[idx] = { role: "user", content: TRUNCATED_MARKER };
|
|
2859
2831
|
}
|
|
2860
2832
|
}
|
|
@@ -2864,9 +2836,115 @@ function enforceContextLimit(messages, maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS
|
|
|
2864
2836
|
// tools/warp_grep/agent/runner.ts
|
|
2865
2837
|
var import_openai2 = __toESM(require("openai"), 1);
|
|
2866
2838
|
init_version();
|
|
2867
|
-
var
|
|
2868
|
-
var parser = new LLMResponseParser();
|
|
2839
|
+
var import_path4 = __toESM(require("path"), 1);
|
|
2869
2840
|
var DEFAULT_API_URL3 = "https://api.morphllm.com";
|
|
2841
|
+
var TOOL_SPECS = [
|
|
2842
|
+
{
|
|
2843
|
+
type: "function",
|
|
2844
|
+
function: {
|
|
2845
|
+
name: "list_directory",
|
|
2846
|
+
description: "Execute ls or find commands to explore directory structure. Max 500 results. Common junk directories are excluded automatically.",
|
|
2847
|
+
parameters: {
|
|
2848
|
+
type: "object",
|
|
2849
|
+
properties: {
|
|
2850
|
+
command: {
|
|
2851
|
+
type: "string",
|
|
2852
|
+
description: "Full ls or find command (e.g. ls -la src/, find . -maxdepth 2 -type f -name '*.py', find . -type d, ls -d */)."
|
|
2853
|
+
}
|
|
2854
|
+
},
|
|
2855
|
+
required: ["command"]
|
|
2856
|
+
}
|
|
2857
|
+
}
|
|
2858
|
+
},
|
|
2859
|
+
{
|
|
2860
|
+
type: "function",
|
|
2861
|
+
function: {
|
|
2862
|
+
name: "grep_search",
|
|
2863
|
+
description: "Search for a regex pattern in file contents. Returns matching lines with file paths and line numbers. Case-insensitive. Respects .gitignore.",
|
|
2864
|
+
parameters: {
|
|
2865
|
+
type: "object",
|
|
2866
|
+
properties: {
|
|
2867
|
+
pattern: {
|
|
2868
|
+
type: "string",
|
|
2869
|
+
description: "Regex pattern to search for in file contents (e.g. 'class\\s+\\w+Error', 'import|require|from', 'def (get|set|update)_user')."
|
|
2870
|
+
},
|
|
2871
|
+
path: {
|
|
2872
|
+
type: "string",
|
|
2873
|
+
description: "File or directory to search in. Defaults to current working directory."
|
|
2874
|
+
},
|
|
2875
|
+
glob: {
|
|
2876
|
+
type: "string",
|
|
2877
|
+
description: "Glob pattern to filter files (e.g. '*.py', '*.{ts,tsx,js,jsx,py,go}', 'src/**/*.go', '!*.test.*')."
|
|
2878
|
+
},
|
|
2879
|
+
limit: {
|
|
2880
|
+
type: "integer",
|
|
2881
|
+
description: "Limit output to first N matching lines. Shows all matches if not specified."
|
|
2882
|
+
}
|
|
2883
|
+
},
|
|
2884
|
+
required: ["pattern"]
|
|
2885
|
+
}
|
|
2886
|
+
}
|
|
2887
|
+
},
|
|
2888
|
+
{
|
|
2889
|
+
type: "function",
|
|
2890
|
+
function: {
|
|
2891
|
+
name: "glob",
|
|
2892
|
+
description: "Find files by name/extension using glob patterns. Returns absolute paths sorted by modification time (newest first). Respects .gitignore. Max 100 results.",
|
|
2893
|
+
parameters: {
|
|
2894
|
+
type: "object",
|
|
2895
|
+
properties: {
|
|
2896
|
+
pattern: {
|
|
2897
|
+
type: "string",
|
|
2898
|
+
description: "Glob pattern to match files (e.g. '*.py', 'src/**/*.js', '*.{ts,tsx}', 'test_*.py')."
|
|
2899
|
+
},
|
|
2900
|
+
path: {
|
|
2901
|
+
type: "string",
|
|
2902
|
+
description: "Directory to search in. Defaults to repository root."
|
|
2903
|
+
}
|
|
2904
|
+
},
|
|
2905
|
+
required: ["pattern"]
|
|
2906
|
+
}
|
|
2907
|
+
}
|
|
2908
|
+
},
|
|
2909
|
+
{
|
|
2910
|
+
type: "function",
|
|
2911
|
+
function: {
|
|
2912
|
+
name: "read",
|
|
2913
|
+
description: "Read entire files or specific line ranges using absolute paths.",
|
|
2914
|
+
parameters: {
|
|
2915
|
+
type: "object",
|
|
2916
|
+
properties: {
|
|
2917
|
+
path: {
|
|
2918
|
+
type: "string",
|
|
2919
|
+
description: "File path to read, using absolute path (e.g. '/home/ubuntu/repo/src/main.py' or windows path)."
|
|
2920
|
+
},
|
|
2921
|
+
lines: {
|
|
2922
|
+
type: "string",
|
|
2923
|
+
description: "Optional line range (e.g. '1-50' or '1-20,45-80'). Omit to read entire file."
|
|
2924
|
+
}
|
|
2925
|
+
},
|
|
2926
|
+
required: ["path"]
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2929
|
+
},
|
|
2930
|
+
{
|
|
2931
|
+
type: "function",
|
|
2932
|
+
function: {
|
|
2933
|
+
name: "finish",
|
|
2934
|
+
description: "Submit final answer with all relevant code locations. Include imports and over-include rather than miss context.",
|
|
2935
|
+
parameters: {
|
|
2936
|
+
type: "object",
|
|
2937
|
+
properties: {
|
|
2938
|
+
files: {
|
|
2939
|
+
type: "string",
|
|
2940
|
+
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."
|
|
2941
|
+
}
|
|
2942
|
+
},
|
|
2943
|
+
required: ["files"]
|
|
2944
|
+
}
|
|
2945
|
+
}
|
|
2946
|
+
}
|
|
2947
|
+
];
|
|
2870
2948
|
async function callModel(messages, model, options = {}) {
|
|
2871
2949
|
const baseUrl = options.morphApiUrl || DEFAULT_API_URL3;
|
|
2872
2950
|
const apiKey = options.morphApiKey || process.env.MORPH_API_KEY || "";
|
|
@@ -2887,8 +2965,9 @@ async function callModel(messages, model, options = {}) {
|
|
|
2887
2965
|
data = await client.chat.completions.create({
|
|
2888
2966
|
model,
|
|
2889
2967
|
temperature: 0,
|
|
2890
|
-
max_tokens:
|
|
2968
|
+
max_tokens: 2048,
|
|
2891
2969
|
messages,
|
|
2970
|
+
tools: TOOL_SPECS,
|
|
2892
2971
|
...options.search_type ? { search_type: options.search_type } : {}
|
|
2893
2972
|
});
|
|
2894
2973
|
} catch (error) {
|
|
@@ -2900,187 +2979,87 @@ async function callModel(messages, model, options = {}) {
|
|
|
2900
2979
|
throw error;
|
|
2901
2980
|
}
|
|
2902
2981
|
const choice = data?.choices?.[0];
|
|
2903
|
-
const
|
|
2904
|
-
if (
|
|
2905
|
-
|
|
2982
|
+
const message = choice?.message;
|
|
2983
|
+
if (!message) {
|
|
2984
|
+
if (attempt === MAX_EMPTY_RETRIES) {
|
|
2985
|
+
throw new Error("Invalid response from model: no message in response");
|
|
2986
|
+
}
|
|
2987
|
+
await new Promise((resolve2) => setTimeout(resolve2, 200));
|
|
2988
|
+
continue;
|
|
2989
|
+
}
|
|
2990
|
+
const toolCalls = (message.tool_calls || []).map((tc) => ({
|
|
2991
|
+
id: tc.id,
|
|
2992
|
+
type: "function",
|
|
2993
|
+
function: { name: tc.function.name, arguments: tc.function.arguments }
|
|
2994
|
+
}));
|
|
2995
|
+
if (message.content || toolCalls.length > 0) {
|
|
2996
|
+
return { content: message.content ?? null, tool_calls: toolCalls };
|
|
2906
2997
|
}
|
|
2907
2998
|
if (attempt === MAX_EMPTY_RETRIES) {
|
|
2908
2999
|
const finishReason = choice?.finish_reason ?? "unknown";
|
|
2909
|
-
const hasToolCalls = Array.isArray(choice?.message?.tool_calls) && choice.message.tool_calls.length > 0;
|
|
2910
|
-
const choicesLen = data?.choices?.length ?? 0;
|
|
2911
|
-
const contentType = content === null ? "null" : content === void 0 ? "undefined" : typeof content;
|
|
2912
3000
|
throw new Error(
|
|
2913
|
-
`Invalid response from model: content
|
|
3001
|
+
`Invalid response from model: no content and no tool_calls, finish_reason=${finishReason}`
|
|
2914
3002
|
);
|
|
2915
3003
|
}
|
|
2916
3004
|
await new Promise((resolve2) => setTimeout(resolve2, 200));
|
|
2917
3005
|
}
|
|
2918
3006
|
throw new Error("Invalid response from model");
|
|
2919
3007
|
}
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
}).catch((e) => {
|
|
2947
|
-
const errMsg = e instanceof Error ? e.message : String(e);
|
|
2948
|
-
console.error(`[warp_grep] Morph API call failed on turn ${turn}:`, errMsg);
|
|
2949
|
-
errors.push({ message: errMsg });
|
|
2950
|
-
return "";
|
|
2951
|
-
});
|
|
2952
|
-
turnMetrics.morph_api_ms = Date.now() - modelCallStart;
|
|
2953
|
-
if (!assistantContent) {
|
|
2954
|
-
console.error(`[warp_grep] Empty response from Morph API on turn ${turn}. Errors so far:`, errors);
|
|
2955
|
-
timings.turns.push(turnMetrics);
|
|
2956
|
-
break;
|
|
2957
|
-
}
|
|
2958
|
-
messages.push({ role: "assistant", content: assistantContent });
|
|
2959
|
-
const toolCalls = parser.parse(assistantContent);
|
|
2960
|
-
if (toolCalls.length === 0) {
|
|
2961
|
-
console.error(`[warp_grep] No tool calls parsed on turn ${turn}. Assistant content (first 500 chars):`, assistantContent.slice(0, 500));
|
|
2962
|
-
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" });
|
|
2963
|
-
terminationReason = "terminated";
|
|
2964
|
-
timings.turns.push(turnMetrics);
|
|
2965
|
-
break;
|
|
2966
|
-
}
|
|
2967
|
-
const finishCalls = toolCalls.filter((c) => c.name === "finish");
|
|
2968
|
-
const grepCalls = toolCalls.filter((c) => c.name === "grep");
|
|
2969
|
-
const listDirCalls = toolCalls.filter((c) => c.name === "list_directory");
|
|
2970
|
-
const readCalls = toolCalls.filter((c) => c.name === "read");
|
|
2971
|
-
const skipCalls = toolCalls.filter((c) => c.name === "_skip");
|
|
2972
|
-
const formatted = [];
|
|
2973
|
-
for (const c of skipCalls) {
|
|
2974
|
-
const msg = c.arguments?.message || "Command skipped due to parsing error";
|
|
2975
|
-
formatted.push(msg);
|
|
2976
|
-
}
|
|
2977
|
-
const allPromises = [];
|
|
2978
|
-
for (const c of grepCalls) {
|
|
2979
|
-
const args = c.arguments ?? {};
|
|
2980
|
-
allPromises.push(
|
|
2981
|
-
toolGrep(provider, args).then(
|
|
2982
|
-
({ output }) => formatAgentToolOutput("grep", args, output),
|
|
2983
|
-
(err) => formatAgentToolOutput("grep", args, String(err), { isError: true })
|
|
2984
|
-
)
|
|
2985
|
-
);
|
|
2986
|
-
}
|
|
2987
|
-
for (const c of listDirCalls) {
|
|
2988
|
-
const args = c.arguments ?? {};
|
|
2989
|
-
allPromises.push(
|
|
2990
|
-
toolListDirectory(provider, args).then(
|
|
2991
|
-
(p) => formatAgentToolOutput("list_directory", args, p),
|
|
2992
|
-
(err) => formatAgentToolOutput("list_directory", args, String(err), { isError: true })
|
|
2993
|
-
)
|
|
2994
|
-
);
|
|
2995
|
-
}
|
|
2996
|
-
for (const c of readCalls) {
|
|
2997
|
-
const args = c.arguments ?? {};
|
|
2998
|
-
allPromises.push(
|
|
2999
|
-
toolRead(provider, args).then(
|
|
3000
|
-
(p) => formatAgentToolOutput("read", args, p),
|
|
3001
|
-
(err) => formatAgentToolOutput("read", args, String(err), { isError: true })
|
|
3002
|
-
)
|
|
3003
|
-
);
|
|
3008
|
+
function safeParseJSON(s) {
|
|
3009
|
+
try {
|
|
3010
|
+
return JSON.parse(s);
|
|
3011
|
+
} catch {
|
|
3012
|
+
return {};
|
|
3013
|
+
}
|
|
3014
|
+
}
|
|
3015
|
+
async function executeTool(provider, name, args, repoRoot) {
|
|
3016
|
+
switch (name) {
|
|
3017
|
+
case "grep_search": {
|
|
3018
|
+
const grepArgs = {
|
|
3019
|
+
pattern: args.pattern,
|
|
3020
|
+
path: args.path || "."
|
|
3021
|
+
};
|
|
3022
|
+
if (args.glob) grepArgs.glob = args.glob;
|
|
3023
|
+
if (args.case_sensitive !== void 0) grepArgs.case_sensitive = args.case_sensitive;
|
|
3024
|
+
const result = await toolGrep(provider, grepArgs);
|
|
3025
|
+
let output = result.output;
|
|
3026
|
+
if (args.limit && typeof args.limit === "number") {
|
|
3027
|
+
const lines = output.split("\n");
|
|
3028
|
+
if (lines.length > args.limit) {
|
|
3029
|
+
output = lines.slice(0, args.limit).join("\n") + `
|
|
3030
|
+
... (truncated at ${args.limit} lines)`;
|
|
3031
|
+
}
|
|
3032
|
+
}
|
|
3033
|
+
return output;
|
|
3004
3034
|
}
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3035
|
+
case "glob": {
|
|
3036
|
+
return toolGlob(provider, {
|
|
3037
|
+
pattern: args.pattern,
|
|
3038
|
+
path: args.path
|
|
3039
|
+
});
|
|
3010
3040
|
}
|
|
3011
|
-
|
|
3012
|
-
const
|
|
3013
|
-
|
|
3014
|
-
messages.push({ role: "user", content: formatted.join("\n") + turnMessage + "\n" + contextBudget });
|
|
3041
|
+
case "list_directory": {
|
|
3042
|
+
const dirPath = extractPathFromCommand(args.command || ".");
|
|
3043
|
+
return toolListDirectory(provider, { path: dirPath }, repoRoot);
|
|
3015
3044
|
}
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
terminationReason = "completed";
|
|
3023
|
-
if (files.length === 0) {
|
|
3024
|
-
const payload2 = textResult || "No relevant code found.";
|
|
3025
|
-
timings.turns.push(turnMetrics);
|
|
3026
|
-
timings.total_ms = Date.now() - totalStart;
|
|
3027
|
-
return {
|
|
3028
|
-
terminationReason: "completed",
|
|
3029
|
-
messages,
|
|
3030
|
-
finish: { payload: payload2, metadata: finishMeta },
|
|
3031
|
-
timings
|
|
3032
|
-
};
|
|
3033
|
-
}
|
|
3034
|
-
break;
|
|
3035
|
-
}
|
|
3036
|
-
}
|
|
3037
|
-
if (terminationReason !== "completed" || !finishMeta) {
|
|
3038
|
-
timings.total_ms = Date.now() - totalStart;
|
|
3039
|
-
return { terminationReason, messages, errors, timings };
|
|
3040
|
-
}
|
|
3041
|
-
const parts = ["Relevant context found:"];
|
|
3042
|
-
for (const f of finishMeta.files) {
|
|
3043
|
-
const ranges = f.lines === "*" ? "*" : Array.isArray(f.lines) ? f.lines.map(([s, e]) => `${s}-${e}`).join(", ") : "*";
|
|
3044
|
-
parts.push(`- ${f.path}: ${ranges}`);
|
|
3045
|
-
}
|
|
3046
|
-
const payload = parts.join("\n");
|
|
3047
|
-
const finishResolutionStart = Date.now();
|
|
3048
|
-
const fileReadErrors = [];
|
|
3049
|
-
const resolved = await readFinishFiles(
|
|
3050
|
-
repoRoot,
|
|
3051
|
-
finishMeta.files,
|
|
3052
|
-
async (p, s, e) => {
|
|
3053
|
-
try {
|
|
3054
|
-
const rr = await provider.read({ path: p, start: s, end: e });
|
|
3055
|
-
return rr.lines.map((l) => {
|
|
3056
|
-
const idx = l.indexOf("|");
|
|
3057
|
-
return idx >= 0 ? l.slice(idx + 1) : l;
|
|
3058
|
-
});
|
|
3059
|
-
} catch (err) {
|
|
3060
|
-
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
3061
|
-
fileReadErrors.push({ path: p, error: errorMsg });
|
|
3062
|
-
console.error(`[warp_grep] Failed to read file: ${p} - ${errorMsg}`);
|
|
3063
|
-
return [`[couldn't find: ${p}]`];
|
|
3045
|
+
case "read": {
|
|
3046
|
+
const readArgs = {
|
|
3047
|
+
path: args.path
|
|
3048
|
+
};
|
|
3049
|
+
if (args.lines && typeof args.lines === "string") {
|
|
3050
|
+
Object.assign(readArgs, parseReadLines(args.lines));
|
|
3064
3051
|
}
|
|
3052
|
+
return toolRead(provider, readArgs);
|
|
3065
3053
|
}
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
if (fileReadErrors.length > 0) {
|
|
3069
|
-
errors.push(...fileReadErrors.map((e) => ({ message: `File read error: ${e.path} - ${e.error}` })));
|
|
3054
|
+
default:
|
|
3055
|
+
return `Unknown tool: ${name}`;
|
|
3070
3056
|
}
|
|
3071
|
-
timings.total_ms = Date.now() - totalStart;
|
|
3072
|
-
return {
|
|
3073
|
-
terminationReason: "completed",
|
|
3074
|
-
messages,
|
|
3075
|
-
finish: { payload, metadata: finishMeta, resolved },
|
|
3076
|
-
timings
|
|
3077
|
-
};
|
|
3078
3057
|
}
|
|
3079
3058
|
async function* runWarpGrepStreaming(config) {
|
|
3080
3059
|
const totalStart = Date.now();
|
|
3081
3060
|
const timeoutMs = config.timeout ?? AGENT_CONFIG.TIMEOUT_MS;
|
|
3082
3061
|
const timings = { turns: [], timeout_ms: timeoutMs };
|
|
3083
|
-
const repoRoot =
|
|
3062
|
+
const repoRoot = import_path4.default.resolve(config.repoRoot || process.cwd());
|
|
3084
3063
|
const model = config.model || DEFAULT_MODEL;
|
|
3085
3064
|
const messages = [];
|
|
3086
3065
|
const maxTurns = AGENT_CONFIG.MAX_TURNS;
|
|
@@ -3096,7 +3075,7 @@ async function* runWarpGrepStreaming(config) {
|
|
|
3096
3075
|
const turnMetrics = { turn, morph_api_ms: 0, local_tools_ms: 0 };
|
|
3097
3076
|
enforceContextLimit(messages);
|
|
3098
3077
|
const modelCallStart = Date.now();
|
|
3099
|
-
const
|
|
3078
|
+
const response = await callModel(messages, model, {
|
|
3100
3079
|
morphApiKey: config.morphApiKey,
|
|
3101
3080
|
morphApiUrl: config.morphApiUrl,
|
|
3102
3081
|
retryConfig: config.retryConfig,
|
|
@@ -3104,90 +3083,45 @@ async function* runWarpGrepStreaming(config) {
|
|
|
3104
3083
|
search_type: config.search_type
|
|
3105
3084
|
}).catch((e) => {
|
|
3106
3085
|
const errMsg = e instanceof Error ? e.message : String(e);
|
|
3107
|
-
console.error(`[warp_grep
|
|
3086
|
+
console.error(`[warp_grep] Morph API call failed on turn ${turn}:`, errMsg);
|
|
3108
3087
|
errors.push({ message: errMsg });
|
|
3109
|
-
return
|
|
3088
|
+
return null;
|
|
3110
3089
|
});
|
|
3111
3090
|
turnMetrics.morph_api_ms = Date.now() - modelCallStart;
|
|
3112
|
-
if (!
|
|
3113
|
-
console.error(`[warp_grep:stream] Empty response from Morph API on turn ${turn}. Errors so far:`, errors);
|
|
3091
|
+
if (!response) {
|
|
3114
3092
|
timings.turns.push(turnMetrics);
|
|
3115
3093
|
break;
|
|
3116
3094
|
}
|
|
3117
|
-
|
|
3118
|
-
|
|
3095
|
+
const toolCalls = response.tool_calls;
|
|
3096
|
+
messages.push({
|
|
3097
|
+
role: "assistant",
|
|
3098
|
+
content: response.content,
|
|
3099
|
+
...toolCalls.length > 0 ? { tool_calls: toolCalls } : {}
|
|
3100
|
+
});
|
|
3119
3101
|
if (toolCalls.length === 0) {
|
|
3120
|
-
console.error(`[warp_grep
|
|
3121
|
-
errors.push({ message: "No tool calls produced by the model.
|
|
3102
|
+
console.error(`[warp_grep] No tool calls on turn ${turn}. Content: ${(response.content || "").slice(0, 500)}`);
|
|
3103
|
+
errors.push({ message: "No tool calls produced by the model." });
|
|
3122
3104
|
terminationReason = "terminated";
|
|
3123
3105
|
timings.turns.push(turnMetrics);
|
|
3124
3106
|
break;
|
|
3125
3107
|
}
|
|
3126
3108
|
yield {
|
|
3127
3109
|
turn,
|
|
3128
|
-
toolCalls: toolCalls.map((
|
|
3129
|
-
name:
|
|
3130
|
-
arguments:
|
|
3110
|
+
toolCalls: toolCalls.map((tc) => ({
|
|
3111
|
+
name: tc.function.name,
|
|
3112
|
+
arguments: safeParseJSON(tc.function.arguments)
|
|
3131
3113
|
}))
|
|
3132
3114
|
};
|
|
3133
|
-
const
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
const formatted = [];
|
|
3139
|
-
for (const c of skipCalls) {
|
|
3140
|
-
const msg = c.arguments?.message || "Command skipped due to parsing error";
|
|
3141
|
-
formatted.push(msg);
|
|
3142
|
-
}
|
|
3143
|
-
const allPromises = [];
|
|
3144
|
-
for (const c of grepCalls) {
|
|
3145
|
-
const args = c.arguments ?? {};
|
|
3146
|
-
allPromises.push(
|
|
3147
|
-
toolGrep(provider, args).then(
|
|
3148
|
-
({ output }) => formatAgentToolOutput("grep", args, output),
|
|
3149
|
-
(err) => formatAgentToolOutput("grep", args, String(err), { isError: true })
|
|
3150
|
-
)
|
|
3151
|
-
);
|
|
3152
|
-
}
|
|
3153
|
-
for (const c of listDirCalls) {
|
|
3154
|
-
const args = c.arguments ?? {};
|
|
3155
|
-
allPromises.push(
|
|
3156
|
-
toolListDirectory(provider, args).then(
|
|
3157
|
-
(p) => formatAgentToolOutput("list_directory", args, p),
|
|
3158
|
-
(err) => formatAgentToolOutput("list_directory", args, String(err), { isError: true })
|
|
3159
|
-
)
|
|
3160
|
-
);
|
|
3161
|
-
}
|
|
3162
|
-
for (const c of readCalls) {
|
|
3163
|
-
const args = c.arguments ?? {};
|
|
3164
|
-
allPromises.push(
|
|
3165
|
-
toolRead(provider, args).then(
|
|
3166
|
-
(p) => formatAgentToolOutput("read", args, p),
|
|
3167
|
-
(err) => formatAgentToolOutput("read", args, String(err), { isError: true })
|
|
3168
|
-
)
|
|
3169
|
-
);
|
|
3170
|
-
}
|
|
3171
|
-
const toolExecStart = Date.now();
|
|
3172
|
-
const allResults = await Promise.all(allPromises);
|
|
3173
|
-
turnMetrics.local_tools_ms = Date.now() - toolExecStart;
|
|
3174
|
-
for (const result of allResults) {
|
|
3175
|
-
formatted.push(result);
|
|
3176
|
-
}
|
|
3177
|
-
if (formatted.length > 0) {
|
|
3178
|
-
const turnMessage = formatTurnMessage(turn, maxTurns);
|
|
3179
|
-
const contextBudget = calculateContextBudget(messages);
|
|
3180
|
-
messages.push({ role: "user", content: formatted.join("\n") + turnMessage + "\n" + contextBudget });
|
|
3181
|
-
}
|
|
3182
|
-
timings.turns.push(turnMetrics);
|
|
3183
|
-
if (finishCalls.length) {
|
|
3184
|
-
const fc = finishCalls[0];
|
|
3185
|
-
const files = fc.arguments?.files ?? [];
|
|
3186
|
-
const textResult = fc.arguments?.textResult;
|
|
3115
|
+
const finishCall = toolCalls.find((tc) => tc.function.name === "finish");
|
|
3116
|
+
if (finishCall) {
|
|
3117
|
+
const args = safeParseJSON(finishCall.function.arguments);
|
|
3118
|
+
const filesStr = args.files || "";
|
|
3119
|
+
const files = parseFinishFiles(filesStr);
|
|
3187
3120
|
finishMeta = { files };
|
|
3188
3121
|
terminationReason = "completed";
|
|
3189
3122
|
if (files.length === 0) {
|
|
3190
|
-
const payload2 =
|
|
3123
|
+
const payload2 = filesStr || "No relevant code found.";
|
|
3124
|
+
timings.turns.push(turnMetrics);
|
|
3191
3125
|
timings.total_ms = Date.now() - totalStart;
|
|
3192
3126
|
return {
|
|
3193
3127
|
terminationReason: "completed",
|
|
@@ -3196,8 +3130,25 @@ async function* runWarpGrepStreaming(config) {
|
|
|
3196
3130
|
timings
|
|
3197
3131
|
};
|
|
3198
3132
|
}
|
|
3133
|
+
timings.turns.push(turnMetrics);
|
|
3199
3134
|
break;
|
|
3200
3135
|
}
|
|
3136
|
+
const toolExecStart = Date.now();
|
|
3137
|
+
const results = await Promise.all(
|
|
3138
|
+
toolCalls.map(async (tc) => {
|
|
3139
|
+
const args = safeParseJSON(tc.function.arguments);
|
|
3140
|
+
const output = await executeTool(provider, tc.function.name, args, repoRoot).catch((err) => String(err));
|
|
3141
|
+
return { tool_call_id: tc.id, content: output };
|
|
3142
|
+
})
|
|
3143
|
+
);
|
|
3144
|
+
turnMetrics.local_tools_ms = Date.now() - toolExecStart;
|
|
3145
|
+
for (const result of results) {
|
|
3146
|
+
messages.push({ role: "tool", tool_call_id: result.tool_call_id, content: result.content });
|
|
3147
|
+
}
|
|
3148
|
+
const turnMsg = formatTurnMessage(turn, maxTurns);
|
|
3149
|
+
const budget = calculateContextBudget(messages);
|
|
3150
|
+
messages.push({ role: "user", content: turnMsg + "\n" + budget });
|
|
3151
|
+
timings.turns.push(turnMetrics);
|
|
3201
3152
|
}
|
|
3202
3153
|
if (terminationReason !== "completed" || !finishMeta) {
|
|
3203
3154
|
timings.total_ms = Date.now() - totalStart;
|
|
@@ -3215,17 +3166,22 @@ async function* runWarpGrepStreaming(config) {
|
|
|
3215
3166
|
repoRoot,
|
|
3216
3167
|
finishMeta.files,
|
|
3217
3168
|
async (p, s, e) => {
|
|
3169
|
+
let resolvedPath = p;
|
|
3170
|
+
if (!p.startsWith(repoRoot)) {
|
|
3171
|
+
const relative2 = p.startsWith("/") ? p.slice(1) : p;
|
|
3172
|
+
resolvedPath = import_path4.default.join(repoRoot, relative2);
|
|
3173
|
+
}
|
|
3218
3174
|
try {
|
|
3219
|
-
const rr = await provider.read({ path:
|
|
3175
|
+
const rr = await provider.read({ path: resolvedPath, start: s, end: e });
|
|
3220
3176
|
return rr.lines.map((l) => {
|
|
3221
3177
|
const idx = l.indexOf("|");
|
|
3222
3178
|
return idx >= 0 ? l.slice(idx + 1) : l;
|
|
3223
3179
|
});
|
|
3224
3180
|
} catch (err) {
|
|
3225
3181
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
3226
|
-
fileReadErrors.push({ path:
|
|
3227
|
-
console.error(`[warp_grep] Failed to read file: ${
|
|
3228
|
-
return [`[couldn't find: ${
|
|
3182
|
+
fileReadErrors.push({ path: resolvedPath, error: errorMsg });
|
|
3183
|
+
console.error(`[warp_grep] Failed to read file: ${resolvedPath} - ${errorMsg}`);
|
|
3184
|
+
return [`[couldn't find: ${resolvedPath}]`];
|
|
3229
3185
|
}
|
|
3230
3186
|
}
|
|
3231
3187
|
);
|
|
@@ -3241,6 +3197,14 @@ async function* runWarpGrepStreaming(config) {
|
|
|
3241
3197
|
timings
|
|
3242
3198
|
};
|
|
3243
3199
|
}
|
|
3200
|
+
async function runWarpGrep(config) {
|
|
3201
|
+
const gen = runWarpGrepStreaming(config);
|
|
3202
|
+
let result = await gen.next();
|
|
3203
|
+
while (!result.done) {
|
|
3204
|
+
result = await gen.next();
|
|
3205
|
+
}
|
|
3206
|
+
return result.value;
|
|
3207
|
+
}
|
|
3244
3208
|
|
|
3245
3209
|
// tools/warp_grep/providers/remote.ts
|
|
3246
3210
|
init_config();
|
|
@@ -3420,6 +3384,35 @@ var RemoteCommandsProvider = class {
|
|
|
3420
3384
|
return [];
|
|
3421
3385
|
}
|
|
3422
3386
|
}
|
|
3387
|
+
/**
|
|
3388
|
+
* Glob search - finds files matching a pattern.
|
|
3389
|
+
* Falls back to a grep --files approach via the listDir command.
|
|
3390
|
+
*/
|
|
3391
|
+
async glob(params) {
|
|
3392
|
+
const searchPath = params.path || this.repoRoot;
|
|
3393
|
+
try {
|
|
3394
|
+
const stdout = await this.commands.listDir(searchPath, 10);
|
|
3395
|
+
const allPaths = (stdout || "").trim().split(/\r?\n/).filter((p) => p.length > 0);
|
|
3396
|
+
const globToRegex = (glob) => {
|
|
3397
|
+
const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
3398
|
+
return new RegExp(escaped);
|
|
3399
|
+
};
|
|
3400
|
+
const regex = globToRegex(params.pattern);
|
|
3401
|
+
const matched = allPaths.filter((p) => {
|
|
3402
|
+
const name = p.split("/").pop() || "";
|
|
3403
|
+
return regex.test(name) && !shouldSkip(name);
|
|
3404
|
+
});
|
|
3405
|
+
const totalFound = matched.length;
|
|
3406
|
+
return { files: matched.slice(0, 100), searchDir: searchPath, totalFound };
|
|
3407
|
+
} catch (error) {
|
|
3408
|
+
return {
|
|
3409
|
+
files: [],
|
|
3410
|
+
searchDir: searchPath,
|
|
3411
|
+
totalFound: 0,
|
|
3412
|
+
error: `[GLOB ERROR] ${error instanceof Error ? error.message : String(error)}`
|
|
3413
|
+
};
|
|
3414
|
+
}
|
|
3415
|
+
}
|
|
3423
3416
|
};
|
|
3424
3417
|
|
|
3425
3418
|
// tools/warp_grep/providers/code_storage_http.ts
|
|
@@ -3446,9 +3439,9 @@ function createCodeStorageHttpCommands(config) {
|
|
|
3446
3439
|
const { baseUrl, repoId, branch } = config;
|
|
3447
3440
|
const encodedRepoId = encodeURIComponent(repoId);
|
|
3448
3441
|
return {
|
|
3449
|
-
grep: (pattern,
|
|
3450
|
-
read: (
|
|
3451
|
-
listDir: (
|
|
3442
|
+
grep: (pattern, path6, glob) => post(`${baseUrl}/api/code-search/${encodedRepoId}/grep`, { pattern, path: path6, glob, branch }, "grep"),
|
|
3443
|
+
read: (path6, start, end) => post(`${baseUrl}/api/code-search/${encodedRepoId}/read`, { path: path6, start, end, branch }, "read"),
|
|
3444
|
+
listDir: (path6, maxDepth) => post(`${baseUrl}/api/code-search/${encodedRepoId}/list`, { path: path6, maxDepth, branch }, "list")
|
|
3452
3445
|
};
|
|
3453
3446
|
}
|
|
3454
3447
|
|
|
@@ -3831,10 +3824,10 @@ var GitHubClient = class {
|
|
|
3831
3824
|
/**
|
|
3832
3825
|
* Make an authenticated API request
|
|
3833
3826
|
*/
|
|
3834
|
-
async request(method,
|
|
3835
|
-
const url = `${this.baseUrl}${
|
|
3827
|
+
async request(method, path6, body) {
|
|
3828
|
+
const url = `${this.baseUrl}${path6}`;
|
|
3836
3829
|
if (this.debug) {
|
|
3837
|
-
console.log(`[GitHub SDK] ${method} ${
|
|
3830
|
+
console.log(`[GitHub SDK] ${method} ${path6}`, body || "");
|
|
3838
3831
|
}
|
|
3839
3832
|
const controller = new AbortController();
|
|
3840
3833
|
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|