@morphllm/morphsdk 0.2.145 → 0.2.146
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-HBIW2XV2.js → chunk-4PBUB77N.js} +2 -2
- package/dist/{chunk-SUE4GYA2.js → chunk-BDHKL3MT.js} +2 -2
- package/dist/{chunk-S54SPKX3.js → chunk-BIQ7234U.js} +2 -2
- package/dist/{chunk-I3J46TSB.js → chunk-DKODF3YG.js} +4 -5
- package/dist/chunk-DKODF3YG.js.map +1 -0
- package/dist/{chunk-MRPASJBX.js → chunk-E45FW5EK.js} +2 -2
- package/dist/{chunk-BXRJYLRS.js → chunk-E4YKEKGW.js} +2 -2
- package/dist/{chunk-G23BI5CQ.js → chunk-EU7OLX4Z.js} +2 -2
- package/dist/chunk-EUHNJMWL.js +409 -0
- package/dist/chunk-EUHNJMWL.js.map +1 -0
- package/dist/{chunk-HE7K2QNQ.js → chunk-FBOJJ3UY.js} +17 -17
- package/dist/{chunk-HYRHI2UL.js → chunk-FIVYDIHX.js} +1 -1
- package/dist/{chunk-GXM3G7Z4.js → chunk-FYO46OT6.js} +2 -2
- package/dist/{chunk-GHPQYSSF.js → chunk-GJUB3ECP.js} +2 -2
- package/dist/{chunk-4Y2NM6JD.js → chunk-HZOTLGJH.js} +2 -42
- package/dist/chunk-HZOTLGJH.js.map +1 -0
- package/dist/{chunk-MTJ3PR4M.js → chunk-I7SFRYTX.js} +2 -2
- package/dist/{chunk-PX7ODEML.js → chunk-J2HIK4GB.js} +2 -2
- package/dist/{chunk-RZXS4ADX.js → chunk-JSWNBCGS.js} +2 -2
- package/dist/{chunk-GXCWKYGU.js → chunk-KYKRRF7E.js} +2 -2
- package/dist/{chunk-N7TTZIBK.js → chunk-MMBQKN4G.js} +2 -2
- package/dist/{chunk-B3AKP3RA.js → chunk-NF2QWJDY.js} +2 -31
- package/dist/chunk-NF2QWJDY.js.map +1 -0
- package/dist/{chunk-JMUAQQJU.js → chunk-NKUSUSVI.js} +3 -3
- package/dist/{chunk-VRV5UYTN.js → chunk-OV57JBMB.js} +2 -2
- package/dist/{chunk-EPIOAODF.js → chunk-Q36MNOFA.js} +2 -2
- package/dist/{chunk-JRBU4UNP.js → chunk-QRSWXP4K.js} +2 -2
- package/dist/{chunk-KELRCMA6.js → chunk-SJYAKVSS.js} +2 -2
- package/dist/{chunk-KELRCMA6.js.map → chunk-SJYAKVSS.js.map} +1 -1
- package/dist/{chunk-IRWHN55G.js → chunk-T564HFSH.js} +1 -1
- package/dist/{chunk-6CFKWZK3.js → chunk-UVNENJ6H.js} +3 -3
- package/dist/{chunk-5FCXLQJU.js → chunk-UYPWKQKV.js} +2 -2
- package/dist/{chunk-BAF33L6C.js → chunk-V73GO5AJ.js} +2 -2
- package/dist/chunk-VCKJ22DX.js +131 -0
- package/dist/chunk-VCKJ22DX.js.map +1 -0
- package/dist/{chunk-XL7R3XN5.js → chunk-VZ6VYRQB.js} +2 -2
- package/dist/{chunk-4LWMPKSB.js → chunk-YIETFYCL.js} +44 -71
- package/dist/chunk-YIETFYCL.js.map +1 -0
- package/dist/client.cjs +438 -426
- package/dist/client.cjs.map +1 -1
- package/dist/client.js +27 -26
- package/dist/edge.cjs +1 -1
- package/dist/edge.cjs.map +1 -1
- package/dist/edge.js +4 -4
- package/dist/{finish-Ddj1MPGt.d.ts → finish-DBKuo8yj.d.ts} +1 -1
- package/dist/index.cjs +438 -445
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +29 -29
- package/dist/modelrouter/core.cjs +1 -1
- package/dist/modelrouter/core.cjs.map +1 -1
- package/dist/modelrouter/core.js +3 -3
- package/dist/modelrouter/index.cjs +1 -1
- package/dist/modelrouter/index.cjs.map +1 -1
- package/dist/modelrouter/index.js +3 -3
- package/dist/subagents/anthropic.cjs +434 -422
- package/dist/subagents/anthropic.cjs.map +1 -1
- package/dist/subagents/anthropic.js +9 -8
- package/dist/subagents/vercel.cjs +434 -422
- package/dist/subagents/vercel.cjs.map +1 -1
- package/dist/subagents/vercel.js +9 -8
- package/dist/tools/browser/anthropic.cjs +1 -1
- package/dist/tools/browser/anthropic.cjs.map +1 -1
- package/dist/tools/browser/anthropic.js +5 -5
- package/dist/tools/browser/core.cjs +1 -1
- package/dist/tools/browser/core.cjs.map +1 -1
- package/dist/tools/browser/core.js +4 -4
- package/dist/tools/browser/index.cjs +1 -1
- package/dist/tools/browser/index.cjs.map +1 -1
- package/dist/tools/browser/index.js +7 -7
- package/dist/tools/browser/openai.cjs +1 -1
- package/dist/tools/browser/openai.cjs.map +1 -1
- package/dist/tools/browser/openai.js +5 -5
- package/dist/tools/browser/profiles/core.cjs +1 -1
- package/dist/tools/browser/profiles/core.cjs.map +1 -1
- package/dist/tools/browser/profiles/core.js +3 -3
- package/dist/tools/browser/profiles/index.cjs +1 -1
- package/dist/tools/browser/profiles/index.cjs.map +1 -1
- package/dist/tools/browser/profiles/index.js +3 -3
- package/dist/tools/browser/vercel.cjs +1 -1
- package/dist/tools/browser/vercel.cjs.map +1 -1
- package/dist/tools/browser/vercel.js +5 -5
- package/dist/tools/codebase_search/anthropic.cjs +1 -1
- package/dist/tools/codebase_search/anthropic.cjs.map +1 -1
- package/dist/tools/codebase_search/anthropic.js +4 -4
- package/dist/tools/codebase_search/core.cjs +1 -1
- package/dist/tools/codebase_search/core.cjs.map +1 -1
- package/dist/tools/codebase_search/core.js +3 -3
- package/dist/tools/codebase_search/index.cjs +1 -1
- package/dist/tools/codebase_search/index.cjs.map +1 -1
- package/dist/tools/codebase_search/index.js +6 -6
- package/dist/tools/codebase_search/openai.cjs +1 -1
- package/dist/tools/codebase_search/openai.cjs.map +1 -1
- package/dist/tools/codebase_search/openai.js +4 -4
- package/dist/tools/codebase_search/vercel.cjs +1 -1
- package/dist/tools/codebase_search/vercel.cjs.map +1 -1
- package/dist/tools/codebase_search/vercel.js +4 -4
- package/dist/tools/fastapply/anthropic.cjs +1 -1
- package/dist/tools/fastapply/anthropic.cjs.map +1 -1
- package/dist/tools/fastapply/anthropic.js +4 -4
- package/dist/tools/fastapply/apply.cjs +1 -1
- package/dist/tools/fastapply/apply.cjs.map +1 -1
- package/dist/tools/fastapply/apply.js +2 -2
- package/dist/tools/fastapply/core.cjs +1 -1
- package/dist/tools/fastapply/core.cjs.map +1 -1
- package/dist/tools/fastapply/core.js +3 -3
- package/dist/tools/fastapply/index.cjs +1 -1
- package/dist/tools/fastapply/index.cjs.map +1 -1
- package/dist/tools/fastapply/index.js +6 -6
- package/dist/tools/fastapply/openai.cjs +1 -1
- package/dist/tools/fastapply/openai.cjs.map +1 -1
- package/dist/tools/fastapply/openai.js +4 -4
- package/dist/tools/fastapply/vercel.cjs +1 -1
- package/dist/tools/fastapply/vercel.cjs.map +1 -1
- package/dist/tools/fastapply/vercel.js +4 -4
- package/dist/tools/index.cjs +1 -1
- package/dist/tools/index.cjs.map +1 -1
- package/dist/tools/index.js +6 -6
- package/dist/tools/utils/resilience.cjs +1 -1
- package/dist/tools/utils/resilience.cjs.map +1 -1
- package/dist/tools/utils/resilience.js +2 -2
- package/dist/tools/warp_grep/agent/config.cjs +3 -4
- package/dist/tools/warp_grep/agent/config.cjs.map +1 -1
- package/dist/tools/warp_grep/agent/config.d.ts +1 -2
- package/dist/tools/warp_grep/agent/config.js +1 -1
- package/dist/tools/warp_grep/agent/parser.cjs +121 -52
- package/dist/tools/warp_grep/agent/parser.cjs.map +1 -1
- package/dist/tools/warp_grep/agent/parser.d.ts +5 -12
- package/dist/tools/warp_grep/agent/parser.js +3 -7
- package/dist/tools/warp_grep/agent/runner.cjs +416 -335
- package/dist/tools/warp_grep/agent/runner.cjs.map +1 -1
- package/dist/tools/warp_grep/agent/runner.d.ts +3 -6
- package/dist/tools/warp_grep/agent/runner.js +6 -5
- package/dist/tools/warp_grep/agent/types.cjs.map +1 -1
- package/dist/tools/warp_grep/agent/types.d.ts +3 -22
- package/dist/tools/warp_grep/anthropic.cjs +434 -422
- package/dist/tools/warp_grep/anthropic.cjs.map +1 -1
- package/dist/tools/warp_grep/anthropic.js +9 -8
- package/dist/tools/warp_grep/client.cjs +434 -422
- package/dist/tools/warp_grep/client.cjs.map +1 -1
- package/dist/tools/warp_grep/client.js +8 -7
- package/dist/tools/warp_grep/gemini.cjs +434 -422
- package/dist/tools/warp_grep/gemini.cjs.map +1 -1
- package/dist/tools/warp_grep/gemini.js +8 -7
- package/dist/tools/warp_grep/gemini.js.map +1 -1
- package/dist/tools/warp_grep/harness.cjs +176 -164
- package/dist/tools/warp_grep/harness.cjs.map +1 -1
- package/dist/tools/warp_grep/harness.d.ts +38 -17
- package/dist/tools/warp_grep/harness.js +14 -15
- package/dist/tools/warp_grep/harness.js.map +1 -1
- package/dist/tools/warp_grep/index.cjs +434 -441
- package/dist/tools/warp_grep/index.cjs.map +1 -1
- package/dist/tools/warp_grep/index.d.ts +1 -1
- package/dist/tools/warp_grep/index.js +10 -10
- package/dist/tools/warp_grep/openai.cjs +434 -422
- package/dist/tools/warp_grep/openai.cjs.map +1 -1
- package/dist/tools/warp_grep/openai.js +9 -8
- package/dist/tools/warp_grep/providers/local.cjs +2 -43
- package/dist/tools/warp_grep/providers/local.cjs.map +1 -1
- package/dist/tools/warp_grep/providers/local.d.ts +1 -5
- package/dist/tools/warp_grep/providers/local.js +2 -2
- package/dist/tools/warp_grep/providers/remote.cjs +2 -32
- package/dist/tools/warp_grep/providers/remote.cjs.map +1 -1
- package/dist/tools/warp_grep/providers/remote.d.ts +1 -9
- package/dist/tools/warp_grep/providers/remote.js +2 -2
- package/dist/tools/warp_grep/providers/types.cjs.map +1 -1
- package/dist/tools/warp_grep/providers/types.d.ts +1 -14
- package/dist/tools/warp_grep/vercel.cjs +434 -422
- package/dist/tools/warp_grep/vercel.cjs.map +1 -1
- package/dist/tools/warp_grep/vercel.js +9 -8
- package/dist/version.cjs +1 -1
- package/dist/version.cjs.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-4LWMPKSB.js.map +0 -1
- package/dist/chunk-4Y2NM6JD.js.map +0 -1
- package/dist/chunk-B3AKP3RA.js.map +0 -1
- package/dist/chunk-CMSHXALI.js +0 -60
- package/dist/chunk-CMSHXALI.js.map +0 -1
- package/dist/chunk-I3J46TSB.js.map +0 -1
- package/dist/chunk-OPEQQGST.js +0 -396
- package/dist/chunk-OPEQQGST.js.map +0 -1
- /package/dist/{chunk-HBIW2XV2.js.map → chunk-4PBUB77N.js.map} +0 -0
- /package/dist/{chunk-SUE4GYA2.js.map → chunk-BDHKL3MT.js.map} +0 -0
- /package/dist/{chunk-S54SPKX3.js.map → chunk-BIQ7234U.js.map} +0 -0
- /package/dist/{chunk-MRPASJBX.js.map → chunk-E45FW5EK.js.map} +0 -0
- /package/dist/{chunk-BXRJYLRS.js.map → chunk-E4YKEKGW.js.map} +0 -0
- /package/dist/{chunk-G23BI5CQ.js.map → chunk-EU7OLX4Z.js.map} +0 -0
- /package/dist/{chunk-HE7K2QNQ.js.map → chunk-FBOJJ3UY.js.map} +0 -0
- /package/dist/{chunk-HYRHI2UL.js.map → chunk-FIVYDIHX.js.map} +0 -0
- /package/dist/{chunk-GXM3G7Z4.js.map → chunk-FYO46OT6.js.map} +0 -0
- /package/dist/{chunk-GHPQYSSF.js.map → chunk-GJUB3ECP.js.map} +0 -0
- /package/dist/{chunk-MTJ3PR4M.js.map → chunk-I7SFRYTX.js.map} +0 -0
- /package/dist/{chunk-PX7ODEML.js.map → chunk-J2HIK4GB.js.map} +0 -0
- /package/dist/{chunk-RZXS4ADX.js.map → chunk-JSWNBCGS.js.map} +0 -0
- /package/dist/{chunk-GXCWKYGU.js.map → chunk-KYKRRF7E.js.map} +0 -0
- /package/dist/{chunk-N7TTZIBK.js.map → chunk-MMBQKN4G.js.map} +0 -0
- /package/dist/{chunk-JMUAQQJU.js.map → chunk-NKUSUSVI.js.map} +0 -0
- /package/dist/{chunk-VRV5UYTN.js.map → chunk-OV57JBMB.js.map} +0 -0
- /package/dist/{chunk-EPIOAODF.js.map → chunk-Q36MNOFA.js.map} +0 -0
- /package/dist/{chunk-JRBU4UNP.js.map → chunk-QRSWXP4K.js.map} +0 -0
- /package/dist/{chunk-IRWHN55G.js.map → chunk-T564HFSH.js.map} +0 -0
- /package/dist/{chunk-6CFKWZK3.js.map → chunk-UVNENJ6H.js.map} +0 -0
- /package/dist/{chunk-5FCXLQJU.js.map → chunk-UYPWKQKV.js.map} +0 -0
- /package/dist/{chunk-BAF33L6C.js.map → chunk-V73GO5AJ.js.map} +0 -0
- /package/dist/{chunk-XL7R3XN5.js.map → chunk-VZ6VYRQB.js.map} +0 -0
|
@@ -42,12 +42,11 @@ var parseEnvTimeout = (envValue, defaultMs) => {
|
|
|
42
42
|
return isNaN(parsed) || parsed <= 0 ? defaultMs : parsed;
|
|
43
43
|
};
|
|
44
44
|
var AGENT_CONFIG = {
|
|
45
|
-
MAX_TURNS:
|
|
45
|
+
MAX_TURNS: 4,
|
|
46
46
|
/** Default timeout for model calls. Can be overridden via MORPH_WARP_GREP_TIMEOUT env var (in ms) */
|
|
47
47
|
TIMEOUT_MS: parseEnvTimeout(process.env.MORPH_WARP_GREP_TIMEOUT, 3e4),
|
|
48
|
-
MAX_CONTEXT_CHARS:
|
|
48
|
+
MAX_CONTEXT_CHARS: 54e4,
|
|
49
49
|
MAX_OUTPUT_LINES: 200,
|
|
50
|
-
MAX_LIST_RESULTS: 500,
|
|
51
50
|
MAX_READ_LINES: 800,
|
|
52
51
|
MAX_LIST_DEPTH: 3,
|
|
53
52
|
LIST_TIMEOUT_MS: 2e3
|
|
@@ -132,61 +131,134 @@ var BUILTIN_EXCLUDES = [
|
|
|
132
131
|
".*"
|
|
133
132
|
];
|
|
134
133
|
var DEFAULT_EXCLUDES = (process.env.MORPH_WARP_GREP_EXCLUDE || "").split(",").map((s) => s.trim()).filter(Boolean).concat(BUILTIN_EXCLUDES);
|
|
135
|
-
var DEFAULT_MODEL = "morph-warp-grep-v2
|
|
134
|
+
var DEFAULT_MODEL = "morph-warp-grep-v2";
|
|
136
135
|
|
|
137
136
|
// tools/warp_grep/agent/parser.ts
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
const trimmed = rangeStr.trim();
|
|
142
|
-
if (!trimmed) continue;
|
|
143
|
-
const parts = trimmed.split("-").map((v) => parseInt(v.trim(), 10));
|
|
144
|
-
if (parts.length >= 2 && Number.isFinite(parts[0]) && Number.isFinite(parts[1])) {
|
|
145
|
-
ranges.push([parts[0], parts[1]]);
|
|
146
|
-
} else if (Number.isFinite(parts[0])) {
|
|
147
|
-
ranges.push([parts[0], parts[0]]);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
if (ranges.length === 1) return { start: ranges[0][0], end: ranges[0][1] };
|
|
151
|
-
if (ranges.length > 1) return { lines: ranges };
|
|
152
|
-
return {};
|
|
137
|
+
var VALID_COMMANDS = ["list_directory", "ripgrep", "read", "finish"];
|
|
138
|
+
function isValidCommand(name) {
|
|
139
|
+
return VALID_COMMANDS.includes(name);
|
|
153
140
|
}
|
|
154
|
-
function
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
141
|
+
function parseQwen3ToolCalls(text) {
|
|
142
|
+
const tools = [];
|
|
143
|
+
const toolCallRegex = /<tool_call>\s*<function=([a-z_][a-z0-9_]*)>([\s\S]*?)<\/function>\s*<\/tool_call>/gi;
|
|
144
|
+
let match;
|
|
145
|
+
while ((match = toolCallRegex.exec(text)) !== null) {
|
|
146
|
+
const funcName = match[1].toLowerCase();
|
|
147
|
+
const body = match[2];
|
|
148
|
+
if (!isValidCommand(funcName)) continue;
|
|
149
|
+
const params = {};
|
|
150
|
+
const paramRegex = /<parameter=([a-z_][a-z0-9_]*)>([\s\S]*?)<\/parameter>/gi;
|
|
151
|
+
let paramMatch;
|
|
152
|
+
while ((paramMatch = paramRegex.exec(body)) !== null) {
|
|
153
|
+
params[paramMatch[1].toLowerCase()] = paramMatch[2].trim();
|
|
163
154
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
155
|
+
if (funcName === "ripgrep") {
|
|
156
|
+
const pattern = params.pattern;
|
|
157
|
+
if (!pattern) continue;
|
|
158
|
+
const args = {
|
|
159
|
+
pattern,
|
|
160
|
+
path: params.path || ".",
|
|
161
|
+
...params.glob && { glob: params.glob },
|
|
162
|
+
...params.context_lines && { context_lines: parseInt(params.context_lines, 10) },
|
|
163
|
+
...params.case_sensitive && { case_sensitive: params.case_sensitive === "true" }
|
|
164
|
+
};
|
|
165
|
+
tools.push({ name: "grep", arguments: args });
|
|
166
|
+
} else if (funcName === "list_directory") {
|
|
167
|
+
const command = params.command;
|
|
168
|
+
const directPath = params.path;
|
|
169
|
+
let dirPath = directPath || ".";
|
|
170
|
+
if (!directPath && command) {
|
|
171
|
+
const tokens = command.trim().split(/\s+/);
|
|
172
|
+
const pathTokens = tokens.slice(1).filter((t) => !t.startsWith("-") && !t.startsWith("|") && !t.startsWith("\\("));
|
|
173
|
+
if (pathTokens.length > 0) {
|
|
174
|
+
dirPath = pathTokens[0];
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
tools.push({ name: "list_directory", arguments: { path: dirPath, pattern: params.pattern || null } });
|
|
178
|
+
} else if (funcName === "read") {
|
|
179
|
+
const filePath = params.path;
|
|
180
|
+
if (!filePath) continue;
|
|
181
|
+
const args = { path: filePath };
|
|
182
|
+
const linesStr = params.lines;
|
|
183
|
+
if (linesStr) {
|
|
184
|
+
const ranges = [];
|
|
185
|
+
for (const rangeStr of linesStr.split(",")) {
|
|
186
|
+
const trimmed = rangeStr.trim();
|
|
187
|
+
if (!trimmed) continue;
|
|
188
|
+
const [s, e] = trimmed.split("-").map((v) => parseInt(v.trim(), 10));
|
|
189
|
+
if (Number.isFinite(s) && Number.isFinite(e)) {
|
|
190
|
+
ranges.push([s, e]);
|
|
191
|
+
} else if (Number.isFinite(s)) {
|
|
192
|
+
ranges.push([s, s]);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (ranges.length === 1) {
|
|
196
|
+
args.start = ranges[0][0];
|
|
197
|
+
args.end = ranges[0][1];
|
|
198
|
+
} else if (ranges.length > 1) {
|
|
199
|
+
args.lines = ranges;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
tools.push({ name: "read", arguments: args });
|
|
203
|
+
} else if (funcName === "finish") {
|
|
204
|
+
if (params.result && !params.files) {
|
|
205
|
+
tools.push({ name: "finish", arguments: { files: [], textResult: params.result } });
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
const filesStr = params.files;
|
|
209
|
+
if (!filesStr) {
|
|
210
|
+
tools.push({ name: "finish", arguments: { files: [], textResult: "No relevant code found." } });
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
const files = [];
|
|
214
|
+
for (const line of filesStr.split("\n")) {
|
|
215
|
+
const trimmed = line.trim();
|
|
216
|
+
if (!trimmed) continue;
|
|
217
|
+
const colonIdx = trimmed.indexOf(":");
|
|
218
|
+
if (colonIdx === -1) {
|
|
219
|
+
files.push({ path: trimmed, lines: "*" });
|
|
220
|
+
} else {
|
|
221
|
+
const filePath = trimmed.slice(0, colonIdx);
|
|
222
|
+
const rangesPart = trimmed.slice(colonIdx + 1);
|
|
223
|
+
const ranges = [];
|
|
224
|
+
for (const rangeStr of rangesPart.split(",")) {
|
|
225
|
+
const rt = rangeStr.trim();
|
|
226
|
+
if (!rt || rt === "*") {
|
|
227
|
+
files.push({ path: filePath, lines: "*" });
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
const [s, e] = rt.split("-").map((v) => parseInt(v.trim(), 10));
|
|
231
|
+
if (Number.isFinite(s) && Number.isFinite(e)) {
|
|
232
|
+
ranges.push([s, e]);
|
|
233
|
+
} else if (Number.isFinite(s)) {
|
|
234
|
+
ranges.push([s, s]);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
if (ranges.length > 0) {
|
|
238
|
+
files.push({ path: filePath, lines: ranges });
|
|
239
|
+
} else if (!files.some((f) => f.path === filePath)) {
|
|
240
|
+
files.push({ path: filePath, lines: "*" });
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (files.length > 0) {
|
|
245
|
+
tools.push({ name: "finish", arguments: { files } });
|
|
246
|
+
} else {
|
|
247
|
+
tools.push({ name: "finish", arguments: { files: [], textResult: filesStr } });
|
|
179
248
|
}
|
|
180
249
|
}
|
|
181
|
-
files.push({ path: filePath, lines: ranges.length > 0 ? ranges : "*" });
|
|
182
250
|
}
|
|
183
|
-
return
|
|
184
|
-
}
|
|
185
|
-
function extractPathFromCommand(command) {
|
|
186
|
-
const tokens = command.trim().split(/\s+/);
|
|
187
|
-
const pathTokens = tokens.slice(1).filter((t) => !t.startsWith("-") && !t.startsWith("|") && !t.startsWith("\\("));
|
|
188
|
-
return pathTokens[0] || ".";
|
|
251
|
+
return tools;
|
|
189
252
|
}
|
|
253
|
+
var LLMResponseParser = class {
|
|
254
|
+
parse(text) {
|
|
255
|
+
if (typeof text !== "string") {
|
|
256
|
+
throw new TypeError("Command text must be a string.");
|
|
257
|
+
}
|
|
258
|
+
const withoutThink = text.replace(/<think>[\s\S]*?<\/think>/gi, "");
|
|
259
|
+
return parseQwen3ToolCalls(withoutThink);
|
|
260
|
+
}
|
|
261
|
+
};
|
|
190
262
|
|
|
191
263
|
// tools/warp_grep/agent/tools/grep.ts
|
|
192
264
|
async function toolGrep(provider, args) {
|
|
@@ -236,42 +308,29 @@ async function toolRead(provider, args) {
|
|
|
236
308
|
}
|
|
237
309
|
|
|
238
310
|
// tools/warp_grep/agent/tools/list_directory.ts
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
const
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
return lines.join("\n");
|
|
254
|
-
}
|
|
255
|
-
return entries.map((e) => e.path).join("\n");
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// tools/warp_grep/agent/tools/glob.ts
|
|
259
|
-
async function toolGlob(provider, args) {
|
|
260
|
-
const res = await provider.glob(args);
|
|
261
|
-
if (res.error) {
|
|
262
|
-
return res.error;
|
|
263
|
-
}
|
|
264
|
-
if (!res.files.length) {
|
|
265
|
-
return "no matches";
|
|
311
|
+
async function toolListDirectory(provider, args) {
|
|
312
|
+
const maxResults = args.maxResults ?? AGENT_CONFIG.MAX_OUTPUT_LINES;
|
|
313
|
+
const initialDepth = args.maxDepth ?? AGENT_CONFIG.MAX_LIST_DEPTH;
|
|
314
|
+
async function getListRecursive(currentDepth) {
|
|
315
|
+
const entries = await provider.listDirectory({
|
|
316
|
+
path: args.path,
|
|
317
|
+
pattern: args.pattern ?? null,
|
|
318
|
+
maxResults,
|
|
319
|
+
maxDepth: currentDepth
|
|
320
|
+
});
|
|
321
|
+
if (entries.length >= maxResults && currentDepth > 0) {
|
|
322
|
+
return getListRecursive(currentDepth - 1);
|
|
323
|
+
}
|
|
324
|
+
return { entries };
|
|
266
325
|
}
|
|
267
|
-
const
|
|
268
|
-
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
326
|
+
const { entries: list } = await getListRecursive(initialDepth);
|
|
327
|
+
if (!list.length) return "empty";
|
|
328
|
+
const tree = list.map((e) => {
|
|
329
|
+
const indent = " ".repeat(e.depth);
|
|
330
|
+
const name = e.type === "dir" ? `${e.name}/` : e.name;
|
|
331
|
+
return `${indent}${name}`;
|
|
332
|
+
}).join("\n");
|
|
333
|
+
return tree;
|
|
275
334
|
}
|
|
276
335
|
|
|
277
336
|
// tools/warp_grep/agent/tools/finish.ts
|
|
@@ -332,20 +391,31 @@ function mergeRanges(ranges) {
|
|
|
332
391
|
return merged;
|
|
333
392
|
}
|
|
334
393
|
|
|
335
|
-
// tools/warp_grep/agent/
|
|
336
|
-
var
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
394
|
+
// tools/warp_grep/agent/formatter.ts
|
|
395
|
+
var ToolOutputFormatter = class {
|
|
396
|
+
format(toolName, _args, output, options = {}) {
|
|
397
|
+
const name = (toolName ?? "").trim();
|
|
398
|
+
if (!name) {
|
|
399
|
+
return "";
|
|
400
|
+
}
|
|
401
|
+
const payload = output?.toString?.()?.trim?.() ?? "";
|
|
402
|
+
const isError = Boolean(options.isError);
|
|
403
|
+
if (!payload && !isError) {
|
|
404
|
+
return "";
|
|
344
405
|
}
|
|
345
|
-
return
|
|
406
|
+
return `<tool_response>
|
|
407
|
+
${payload}
|
|
408
|
+
</tool_response>`;
|
|
346
409
|
}
|
|
347
|
-
|
|
410
|
+
};
|
|
411
|
+
var sharedFormatter = new ToolOutputFormatter();
|
|
412
|
+
function formatAgentToolOutput(toolName, args, output, options = {}) {
|
|
413
|
+
return sharedFormatter.format(toolName, args, output, options);
|
|
348
414
|
}
|
|
415
|
+
|
|
416
|
+
// tools/warp_grep/agent/helpers.ts
|
|
417
|
+
var import_path = __toESM(require("path"), 1);
|
|
418
|
+
var TRUNCATED_MARKER = "[truncated for context limit]";
|
|
349
419
|
function formatTurnMessage(turnsUsed, maxTurns) {
|
|
350
420
|
const turnsRemaining = maxTurns - turnsUsed;
|
|
351
421
|
if (turnsRemaining === 1) {
|
|
@@ -356,7 +426,7 @@ You have used ${turnsUsed} turns, you only have 1 turn remaining. You have run o
|
|
|
356
426
|
You have used ${turnsUsed} turn${turnsUsed === 1 ? "" : "s"} and have ${turnsRemaining} remaining`;
|
|
357
427
|
}
|
|
358
428
|
function calculateContextBudget(messages) {
|
|
359
|
-
const totalChars = messages.reduce((sum, m) => sum +
|
|
429
|
+
const totalChars = messages.reduce((sum, m) => sum + m.content.length, 0);
|
|
360
430
|
const maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS;
|
|
361
431
|
const percent = Math.round(totalChars / maxChars * 100);
|
|
362
432
|
const usedK = Math.round(totalChars / 1e3);
|
|
@@ -365,21 +435,24 @@ function calculateContextBudget(messages) {
|
|
|
365
435
|
}
|
|
366
436
|
async function buildInitialState(repoRoot, searchTerm, provider, options) {
|
|
367
437
|
const budget = calculateContextBudget([]);
|
|
368
|
-
const turnTag = `
|
|
438
|
+
const turnTag = `Turn 0/${AGENT_CONFIG.MAX_TURNS}`;
|
|
369
439
|
const treeDepth = options?.search_type === "node_modules" ? 1 : 2;
|
|
370
|
-
const absRoot = import_path2.default.resolve(repoRoot);
|
|
371
440
|
try {
|
|
372
441
|
const entries = await provider.listDirectory({
|
|
373
442
|
path: ".",
|
|
374
443
|
maxResults: AGENT_CONFIG.MAX_OUTPUT_LINES,
|
|
375
444
|
maxDepth: treeDepth
|
|
376
445
|
});
|
|
377
|
-
const
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
446
|
+
const treeLines = entries.map((e) => {
|
|
447
|
+
const indent = " ".repeat(e.depth);
|
|
448
|
+
const name = e.type === "dir" ? `${e.name}/` : e.name;
|
|
449
|
+
return `${indent}${name}`;
|
|
450
|
+
});
|
|
451
|
+
const repoName = import_path.default.basename(repoRoot);
|
|
452
|
+
const treeOutput = treeLines.length > 0 ? `${repoName}/
|
|
453
|
+
${treeLines.join("\n")}` : `${repoName}/`;
|
|
381
454
|
return `<repo_structure>
|
|
382
|
-
${
|
|
455
|
+
${treeOutput}
|
|
383
456
|
</repo_structure>
|
|
384
457
|
|
|
385
458
|
<search_string>
|
|
@@ -388,8 +461,9 @@ ${searchTerm}
|
|
|
388
461
|
${budget}
|
|
389
462
|
${turnTag}`;
|
|
390
463
|
} catch {
|
|
464
|
+
const repoName = import_path.default.basename(repoRoot);
|
|
391
465
|
return `<repo_structure>
|
|
392
|
-
${
|
|
466
|
+
${repoName}/
|
|
393
467
|
</repo_structure>
|
|
394
468
|
|
|
395
469
|
<search_string>
|
|
@@ -400,32 +474,26 @@ ${turnTag}`;
|
|
|
400
474
|
}
|
|
401
475
|
}
|
|
402
476
|
function enforceContextLimit(messages, maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS) {
|
|
403
|
-
const getTotalChars = () => messages.reduce((sum, m) => sum +
|
|
477
|
+
const getTotalChars = () => messages.reduce((sum, m) => sum + m.content.length, 0);
|
|
404
478
|
if (getTotalChars() <= maxChars) {
|
|
405
479
|
return messages;
|
|
406
480
|
}
|
|
407
|
-
const
|
|
481
|
+
const userIndices = [];
|
|
408
482
|
let firstUserSkipped = false;
|
|
409
483
|
for (let i = 0; i < messages.length; i++) {
|
|
410
|
-
|
|
411
|
-
if (m.role === "tool") {
|
|
412
|
-
truncatableIndices.push(i);
|
|
413
|
-
} else if (m.role === "user") {
|
|
484
|
+
if (messages[i].role === "user") {
|
|
414
485
|
if (!firstUserSkipped) {
|
|
415
486
|
firstUserSkipped = true;
|
|
416
487
|
continue;
|
|
417
488
|
}
|
|
418
|
-
|
|
489
|
+
userIndices.push(i);
|
|
419
490
|
}
|
|
420
491
|
}
|
|
421
|
-
for (const idx of
|
|
492
|
+
for (const idx of userIndices) {
|
|
422
493
|
if (getTotalChars() <= maxChars) {
|
|
423
494
|
break;
|
|
424
495
|
}
|
|
425
|
-
|
|
426
|
-
if (m.role === "tool" && m.content !== TRUNCATED_MARKER) {
|
|
427
|
-
messages[idx] = { role: "tool", tool_call_id: m.tool_call_id, content: TRUNCATED_MARKER };
|
|
428
|
-
} else if (m.role === "user" && m.content !== TRUNCATED_MARKER) {
|
|
496
|
+
if (messages[idx].content !== TRUNCATED_MARKER) {
|
|
429
497
|
messages[idx] = { role: "user", content: TRUNCATED_MARKER };
|
|
430
498
|
}
|
|
431
499
|
}
|
|
@@ -438,7 +506,7 @@ var import_openai = __toESM(require("openai"), 1);
|
|
|
438
506
|
// package.json
|
|
439
507
|
var package_default = {
|
|
440
508
|
name: "@morphllm/morphsdk",
|
|
441
|
-
version: "0.2.
|
|
509
|
+
version: "0.2.146",
|
|
442
510
|
description: "TypeScript SDK and CLI for Morph Fast Apply integration",
|
|
443
511
|
type: "module",
|
|
444
512
|
main: "./dist/index.cjs",
|
|
@@ -677,115 +745,9 @@ var package_default = {
|
|
|
677
745
|
var SDK_VERSION = package_default.version;
|
|
678
746
|
|
|
679
747
|
// tools/warp_grep/agent/runner.ts
|
|
680
|
-
var
|
|
748
|
+
var import_path2 = __toESM(require("path"), 1);
|
|
749
|
+
var parser = new LLMResponseParser();
|
|
681
750
|
var DEFAULT_API_URL = "https://api.morphllm.com";
|
|
682
|
-
var TOOL_SPECS = [
|
|
683
|
-
{
|
|
684
|
-
type: "function",
|
|
685
|
-
function: {
|
|
686
|
-
name: "list_directory",
|
|
687
|
-
description: "Execute ls or find commands to explore directory structure. Max 500 results. Common junk directories are excluded automatically.",
|
|
688
|
-
parameters: {
|
|
689
|
-
type: "object",
|
|
690
|
-
properties: {
|
|
691
|
-
command: {
|
|
692
|
-
type: "string",
|
|
693
|
-
description: "Full ls or find command (e.g. ls -la src/, find . -maxdepth 2 -type f -name '*.py', find . -type d, ls -d */)."
|
|
694
|
-
}
|
|
695
|
-
},
|
|
696
|
-
required: ["command"]
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
},
|
|
700
|
-
{
|
|
701
|
-
type: "function",
|
|
702
|
-
function: {
|
|
703
|
-
name: "grep_search",
|
|
704
|
-
description: "Search for a regex pattern in file contents. Returns matching lines with file paths and line numbers. Case-insensitive. Respects .gitignore.",
|
|
705
|
-
parameters: {
|
|
706
|
-
type: "object",
|
|
707
|
-
properties: {
|
|
708
|
-
pattern: {
|
|
709
|
-
type: "string",
|
|
710
|
-
description: "Regex pattern to search for in file contents (e.g. 'class\\s+\\w+Error', 'import|require|from', 'def (get|set|update)_user')."
|
|
711
|
-
},
|
|
712
|
-
path: {
|
|
713
|
-
type: "string",
|
|
714
|
-
description: "File or directory to search in. Defaults to current working directory."
|
|
715
|
-
},
|
|
716
|
-
glob: {
|
|
717
|
-
type: "string",
|
|
718
|
-
description: "Glob pattern to filter files (e.g. '*.py', '*.{ts,tsx,js,jsx,py,go}', 'src/**/*.go', '!*.test.*')."
|
|
719
|
-
},
|
|
720
|
-
limit: {
|
|
721
|
-
type: "integer",
|
|
722
|
-
description: "Limit output to first N matching lines. Shows all matches if not specified."
|
|
723
|
-
}
|
|
724
|
-
},
|
|
725
|
-
required: ["pattern"]
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
},
|
|
729
|
-
{
|
|
730
|
-
type: "function",
|
|
731
|
-
function: {
|
|
732
|
-
name: "glob",
|
|
733
|
-
description: "Find files by name/extension using glob patterns. Returns absolute paths sorted by modification time (newest first). Respects .gitignore. Max 100 results.",
|
|
734
|
-
parameters: {
|
|
735
|
-
type: "object",
|
|
736
|
-
properties: {
|
|
737
|
-
pattern: {
|
|
738
|
-
type: "string",
|
|
739
|
-
description: "Glob pattern to match files (e.g. '*.py', 'src/**/*.js', '*.{ts,tsx}', 'test_*.py')."
|
|
740
|
-
},
|
|
741
|
-
path: {
|
|
742
|
-
type: "string",
|
|
743
|
-
description: "Directory to search in. Defaults to repository root."
|
|
744
|
-
}
|
|
745
|
-
},
|
|
746
|
-
required: ["pattern"]
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
},
|
|
750
|
-
{
|
|
751
|
-
type: "function",
|
|
752
|
-
function: {
|
|
753
|
-
name: "read",
|
|
754
|
-
description: "Read entire files or specific line ranges using absolute paths.",
|
|
755
|
-
parameters: {
|
|
756
|
-
type: "object",
|
|
757
|
-
properties: {
|
|
758
|
-
path: {
|
|
759
|
-
type: "string",
|
|
760
|
-
description: "File path to read, using absolute path (e.g. '/home/ubuntu/repo/src/main.py' or windows path)."
|
|
761
|
-
},
|
|
762
|
-
lines: {
|
|
763
|
-
type: "string",
|
|
764
|
-
description: "Optional line range (e.g. '1-50' or '1-20,45-80'). Omit to read entire file."
|
|
765
|
-
}
|
|
766
|
-
},
|
|
767
|
-
required: ["path"]
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
},
|
|
771
|
-
{
|
|
772
|
-
type: "function",
|
|
773
|
-
function: {
|
|
774
|
-
name: "finish",
|
|
775
|
-
description: "Submit final answer with all relevant code locations. Include imports and over-include rather than miss context.",
|
|
776
|
-
parameters: {
|
|
777
|
-
type: "object",
|
|
778
|
-
properties: {
|
|
779
|
-
files: {
|
|
780
|
-
type: "string",
|
|
781
|
-
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."
|
|
782
|
-
}
|
|
783
|
-
},
|
|
784
|
-
required: ["files"]
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
];
|
|
789
751
|
async function callModel(messages, model, options = {}) {
|
|
790
752
|
const baseUrl = options.morphApiUrl || DEFAULT_API_URL;
|
|
791
753
|
const apiKey = options.morphApiKey || process.env.MORPH_API_KEY || "";
|
|
@@ -806,9 +768,8 @@ async function callModel(messages, model, options = {}) {
|
|
|
806
768
|
data = await client.chat.completions.create({
|
|
807
769
|
model,
|
|
808
770
|
temperature: 0,
|
|
809
|
-
max_tokens:
|
|
771
|
+
max_tokens: 1024,
|
|
810
772
|
messages,
|
|
811
|
-
tools: TOOL_SPECS,
|
|
812
773
|
...options.search_type ? { search_type: options.search_type } : {}
|
|
813
774
|
});
|
|
814
775
|
} catch (error) {
|
|
@@ -820,87 +781,187 @@ async function callModel(messages, model, options = {}) {
|
|
|
820
781
|
throw error;
|
|
821
782
|
}
|
|
822
783
|
const choice = data?.choices?.[0];
|
|
823
|
-
const
|
|
824
|
-
if (
|
|
825
|
-
|
|
826
|
-
throw new Error("Invalid response from model: no message in response");
|
|
827
|
-
}
|
|
828
|
-
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
829
|
-
continue;
|
|
830
|
-
}
|
|
831
|
-
const toolCalls = (message.tool_calls || []).map((tc) => ({
|
|
832
|
-
id: tc.id,
|
|
833
|
-
type: "function",
|
|
834
|
-
function: { name: tc.function.name, arguments: tc.function.arguments }
|
|
835
|
-
}));
|
|
836
|
-
if (message.content || toolCalls.length > 0) {
|
|
837
|
-
return { content: message.content ?? null, tool_calls: toolCalls };
|
|
784
|
+
const content = choice?.message?.content;
|
|
785
|
+
if (content && typeof content === "string") {
|
|
786
|
+
return content;
|
|
838
787
|
}
|
|
839
788
|
if (attempt === MAX_EMPTY_RETRIES) {
|
|
840
789
|
const finishReason = choice?.finish_reason ?? "unknown";
|
|
790
|
+
const hasToolCalls = Array.isArray(choice?.message?.tool_calls) && choice.message.tool_calls.length > 0;
|
|
791
|
+
const choicesLen = data?.choices?.length ?? 0;
|
|
792
|
+
const contentType = content === null ? "null" : content === void 0 ? "undefined" : typeof content;
|
|
841
793
|
throw new Error(
|
|
842
|
-
`Invalid response from model:
|
|
794
|
+
`Invalid response from model: content=${contentType}, finish_reason=${finishReason}, has_tool_calls=${hasToolCalls}, choices_length=${choicesLen}`
|
|
843
795
|
);
|
|
844
796
|
}
|
|
845
797
|
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
846
798
|
}
|
|
847
799
|
throw new Error("Invalid response from model");
|
|
848
800
|
}
|
|
849
|
-
function
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
801
|
+
async function runWarpGrep(config) {
|
|
802
|
+
const totalStart = Date.now();
|
|
803
|
+
const timeoutMs = config.timeout ?? AGENT_CONFIG.TIMEOUT_MS;
|
|
804
|
+
const timings = { turns: [], timeout_ms: timeoutMs };
|
|
805
|
+
const repoRoot = import_path2.default.resolve(config.repoRoot || process.cwd());
|
|
806
|
+
const model = config.model || DEFAULT_MODEL;
|
|
807
|
+
const messages = [];
|
|
808
|
+
const maxTurns = AGENT_CONFIG.MAX_TURNS;
|
|
809
|
+
const initialStateStart = Date.now();
|
|
810
|
+
const initialState = await buildInitialState(repoRoot, config.searchTerm, config.provider, { search_type: config.search_type });
|
|
811
|
+
timings.initial_state_ms = Date.now() - initialStateStart;
|
|
812
|
+
messages.push({ role: "user", content: initialState });
|
|
813
|
+
const provider = config.provider;
|
|
814
|
+
const errors = [];
|
|
815
|
+
let finishMeta;
|
|
816
|
+
let terminationReason = "terminated";
|
|
817
|
+
for (let turn = 1; turn <= maxTurns; turn += 1) {
|
|
818
|
+
const turnMetrics = { turn, morph_api_ms: 0, local_tools_ms: 0 };
|
|
819
|
+
enforceContextLimit(messages);
|
|
820
|
+
const modelCallStart = Date.now();
|
|
821
|
+
const assistantContent = await callModel(messages, model, {
|
|
822
|
+
morphApiKey: config.morphApiKey,
|
|
823
|
+
morphApiUrl: config.morphApiUrl,
|
|
824
|
+
retryConfig: config.retryConfig,
|
|
825
|
+
timeout: timeoutMs,
|
|
826
|
+
search_type: config.search_type
|
|
827
|
+
}).catch((e) => {
|
|
828
|
+
const errMsg = e instanceof Error ? e.message : String(e);
|
|
829
|
+
console.error(`[warp_grep] Morph API call failed on turn ${turn}:`, errMsg);
|
|
830
|
+
errors.push({ message: errMsg });
|
|
831
|
+
return "";
|
|
832
|
+
});
|
|
833
|
+
turnMetrics.morph_api_ms = Date.now() - modelCallStart;
|
|
834
|
+
if (!assistantContent) {
|
|
835
|
+
console.error(`[warp_grep] Empty response from Morph API on turn ${turn}. Errors so far:`, errors);
|
|
836
|
+
timings.turns.push(turnMetrics);
|
|
837
|
+
break;
|
|
875
838
|
}
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
});
|
|
839
|
+
messages.push({ role: "assistant", content: assistantContent });
|
|
840
|
+
const toolCalls = parser.parse(assistantContent);
|
|
841
|
+
if (toolCalls.length === 0) {
|
|
842
|
+
console.error(`[warp_grep] No tool calls parsed on turn ${turn}. Assistant content (first 500 chars):`, assistantContent.slice(0, 500));
|
|
843
|
+
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" });
|
|
844
|
+
terminationReason = "terminated";
|
|
845
|
+
timings.turns.push(turnMetrics);
|
|
846
|
+
break;
|
|
881
847
|
}
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
848
|
+
const finishCalls = toolCalls.filter((c) => c.name === "finish");
|
|
849
|
+
const grepCalls = toolCalls.filter((c) => c.name === "grep");
|
|
850
|
+
const listDirCalls = toolCalls.filter((c) => c.name === "list_directory");
|
|
851
|
+
const readCalls = toolCalls.filter((c) => c.name === "read");
|
|
852
|
+
const skipCalls = toolCalls.filter((c) => c.name === "_skip");
|
|
853
|
+
const formatted = [];
|
|
854
|
+
for (const c of skipCalls) {
|
|
855
|
+
const msg = c.arguments?.message || "Command skipped due to parsing error";
|
|
856
|
+
formatted.push(msg);
|
|
885
857
|
}
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
858
|
+
const allPromises = [];
|
|
859
|
+
for (const c of grepCalls) {
|
|
860
|
+
const args = c.arguments ?? {};
|
|
861
|
+
allPromises.push(
|
|
862
|
+
toolGrep(provider, args).then(
|
|
863
|
+
({ output }) => formatAgentToolOutput("grep", args, output),
|
|
864
|
+
(err) => formatAgentToolOutput("grep", args, String(err), { isError: true })
|
|
865
|
+
)
|
|
866
|
+
);
|
|
867
|
+
}
|
|
868
|
+
for (const c of listDirCalls) {
|
|
869
|
+
const args = c.arguments ?? {};
|
|
870
|
+
allPromises.push(
|
|
871
|
+
toolListDirectory(provider, args).then(
|
|
872
|
+
(p) => formatAgentToolOutput("list_directory", args, p),
|
|
873
|
+
(err) => formatAgentToolOutput("list_directory", args, String(err), { isError: true })
|
|
874
|
+
)
|
|
875
|
+
);
|
|
876
|
+
}
|
|
877
|
+
for (const c of readCalls) {
|
|
878
|
+
const args = c.arguments ?? {};
|
|
879
|
+
allPromises.push(
|
|
880
|
+
toolRead(provider, args).then(
|
|
881
|
+
(p) => formatAgentToolOutput("read", args, p),
|
|
882
|
+
(err) => formatAgentToolOutput("read", args, String(err), { isError: true })
|
|
883
|
+
)
|
|
884
|
+
);
|
|
885
|
+
}
|
|
886
|
+
const toolExecStart = Date.now();
|
|
887
|
+
const allResults = await Promise.all(allPromises);
|
|
888
|
+
turnMetrics.local_tools_ms = Date.now() - toolExecStart;
|
|
889
|
+
for (const result of allResults) {
|
|
890
|
+
formatted.push(result);
|
|
891
|
+
}
|
|
892
|
+
if (formatted.length > 0) {
|
|
893
|
+
const turnMessage = formatTurnMessage(turn, maxTurns);
|
|
894
|
+
const contextBudget = calculateContextBudget(messages);
|
|
895
|
+
messages.push({ role: "user", content: formatted.join("\n") + turnMessage + "\n" + contextBudget });
|
|
896
|
+
}
|
|
897
|
+
timings.turns.push(turnMetrics);
|
|
898
|
+
if (finishCalls.length) {
|
|
899
|
+
const fc = finishCalls[0];
|
|
900
|
+
const files = fc.arguments?.files ?? [];
|
|
901
|
+
const textResult = fc.arguments?.textResult;
|
|
902
|
+
finishMeta = { files };
|
|
903
|
+
terminationReason = "completed";
|
|
904
|
+
if (files.length === 0) {
|
|
905
|
+
const payload2 = textResult || "No relevant code found.";
|
|
906
|
+
timings.turns.push(turnMetrics);
|
|
907
|
+
timings.total_ms = Date.now() - totalStart;
|
|
908
|
+
return {
|
|
909
|
+
terminationReason: "completed",
|
|
910
|
+
messages,
|
|
911
|
+
finish: { payload: payload2, metadata: finishMeta },
|
|
912
|
+
timings
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
break;
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
if (terminationReason !== "completed" || !finishMeta) {
|
|
919
|
+
timings.total_ms = Date.now() - totalStart;
|
|
920
|
+
return { terminationReason, messages, errors, timings };
|
|
921
|
+
}
|
|
922
|
+
const parts = ["Relevant context found:"];
|
|
923
|
+
for (const f of finishMeta.files) {
|
|
924
|
+
const ranges = f.lines === "*" ? "*" : Array.isArray(f.lines) ? f.lines.map(([s, e]) => `${s}-${e}`).join(", ") : "*";
|
|
925
|
+
parts.push(`- ${f.path}: ${ranges}`);
|
|
926
|
+
}
|
|
927
|
+
const payload = parts.join("\n");
|
|
928
|
+
const finishResolutionStart = Date.now();
|
|
929
|
+
const fileReadErrors = [];
|
|
930
|
+
const resolved = await readFinishFiles(
|
|
931
|
+
repoRoot,
|
|
932
|
+
finishMeta.files,
|
|
933
|
+
async (p, s, e) => {
|
|
934
|
+
try {
|
|
935
|
+
const rr = await provider.read({ path: p, start: s, end: e });
|
|
936
|
+
return rr.lines.map((l) => {
|
|
937
|
+
const idx = l.indexOf("|");
|
|
938
|
+
return idx >= 0 ? l.slice(idx + 1) : l;
|
|
939
|
+
});
|
|
940
|
+
} catch (err) {
|
|
941
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
942
|
+
fileReadErrors.push({ path: p, error: errorMsg });
|
|
943
|
+
console.error(`[warp_grep] Failed to read file: ${p} - ${errorMsg}`);
|
|
944
|
+
return [`[couldn't find: ${p}]`];
|
|
892
945
|
}
|
|
893
|
-
return toolRead(provider, readArgs);
|
|
894
946
|
}
|
|
895
|
-
|
|
896
|
-
|
|
947
|
+
);
|
|
948
|
+
timings.finish_resolution_ms = Date.now() - finishResolutionStart;
|
|
949
|
+
if (fileReadErrors.length > 0) {
|
|
950
|
+
errors.push(...fileReadErrors.map((e) => ({ message: `File read error: ${e.path} - ${e.error}` })));
|
|
897
951
|
}
|
|
952
|
+
timings.total_ms = Date.now() - totalStart;
|
|
953
|
+
return {
|
|
954
|
+
terminationReason: "completed",
|
|
955
|
+
messages,
|
|
956
|
+
finish: { payload, metadata: finishMeta, resolved },
|
|
957
|
+
timings
|
|
958
|
+
};
|
|
898
959
|
}
|
|
899
960
|
async function* runWarpGrepStreaming(config) {
|
|
900
961
|
const totalStart = Date.now();
|
|
901
962
|
const timeoutMs = config.timeout ?? AGENT_CONFIG.TIMEOUT_MS;
|
|
902
963
|
const timings = { turns: [], timeout_ms: timeoutMs };
|
|
903
|
-
const repoRoot =
|
|
964
|
+
const repoRoot = import_path2.default.resolve(config.repoRoot || process.cwd());
|
|
904
965
|
const model = config.model || DEFAULT_MODEL;
|
|
905
966
|
const messages = [];
|
|
906
967
|
const maxTurns = AGENT_CONFIG.MAX_TURNS;
|
|
@@ -916,7 +977,7 @@ async function* runWarpGrepStreaming(config) {
|
|
|
916
977
|
const turnMetrics = { turn, morph_api_ms: 0, local_tools_ms: 0 };
|
|
917
978
|
enforceContextLimit(messages);
|
|
918
979
|
const modelCallStart = Date.now();
|
|
919
|
-
const
|
|
980
|
+
const assistantContent = await callModel(messages, model, {
|
|
920
981
|
morphApiKey: config.morphApiKey,
|
|
921
982
|
morphApiUrl: config.morphApiUrl,
|
|
922
983
|
retryConfig: config.retryConfig,
|
|
@@ -924,45 +985,90 @@ async function* runWarpGrepStreaming(config) {
|
|
|
924
985
|
search_type: config.search_type
|
|
925
986
|
}).catch((e) => {
|
|
926
987
|
const errMsg = e instanceof Error ? e.message : String(e);
|
|
927
|
-
console.error(`[warp_grep] Morph API call failed on turn ${turn}:`, errMsg);
|
|
988
|
+
console.error(`[warp_grep:stream] Morph API call failed on turn ${turn}:`, errMsg);
|
|
928
989
|
errors.push({ message: errMsg });
|
|
929
|
-
return
|
|
990
|
+
return "";
|
|
930
991
|
});
|
|
931
992
|
turnMetrics.morph_api_ms = Date.now() - modelCallStart;
|
|
932
|
-
if (!
|
|
993
|
+
if (!assistantContent) {
|
|
994
|
+
console.error(`[warp_grep:stream] Empty response from Morph API on turn ${turn}. Errors so far:`, errors);
|
|
933
995
|
timings.turns.push(turnMetrics);
|
|
934
996
|
break;
|
|
935
997
|
}
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
role: "assistant",
|
|
939
|
-
content: response.content,
|
|
940
|
-
...toolCalls.length > 0 ? { tool_calls: toolCalls } : {}
|
|
941
|
-
});
|
|
998
|
+
messages.push({ role: "assistant", content: assistantContent });
|
|
999
|
+
const toolCalls = parser.parse(assistantContent);
|
|
942
1000
|
if (toolCalls.length === 0) {
|
|
943
|
-
console.error(`[warp_grep] No tool calls on turn ${turn}.
|
|
944
|
-
errors.push({ message: "No tool calls produced by the model." });
|
|
1001
|
+
console.error(`[warp_grep:stream] No tool calls parsed on turn ${turn}. Assistant content (first 500 chars):`, assistantContent.slice(0, 500));
|
|
1002
|
+
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" });
|
|
945
1003
|
terminationReason = "terminated";
|
|
946
1004
|
timings.turns.push(turnMetrics);
|
|
947
1005
|
break;
|
|
948
1006
|
}
|
|
949
1007
|
yield {
|
|
950
1008
|
turn,
|
|
951
|
-
toolCalls: toolCalls.map((
|
|
952
|
-
name:
|
|
953
|
-
arguments:
|
|
1009
|
+
toolCalls: toolCalls.map((c) => ({
|
|
1010
|
+
name: c.name,
|
|
1011
|
+
arguments: c.arguments ?? {}
|
|
954
1012
|
}))
|
|
955
1013
|
};
|
|
956
|
-
const
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
1014
|
+
const finishCalls = toolCalls.filter((c) => c.name === "finish");
|
|
1015
|
+
const grepCalls = toolCalls.filter((c) => c.name === "grep");
|
|
1016
|
+
const listDirCalls = toolCalls.filter((c) => c.name === "list_directory");
|
|
1017
|
+
const readCalls = toolCalls.filter((c) => c.name === "read");
|
|
1018
|
+
const skipCalls = toolCalls.filter((c) => c.name === "_skip");
|
|
1019
|
+
const formatted = [];
|
|
1020
|
+
for (const c of skipCalls) {
|
|
1021
|
+
const msg = c.arguments?.message || "Command skipped due to parsing error";
|
|
1022
|
+
formatted.push(msg);
|
|
1023
|
+
}
|
|
1024
|
+
const allPromises = [];
|
|
1025
|
+
for (const c of grepCalls) {
|
|
1026
|
+
const args = c.arguments ?? {};
|
|
1027
|
+
allPromises.push(
|
|
1028
|
+
toolGrep(provider, args).then(
|
|
1029
|
+
({ output }) => formatAgentToolOutput("grep", args, output),
|
|
1030
|
+
(err) => formatAgentToolOutput("grep", args, String(err), { isError: true })
|
|
1031
|
+
)
|
|
1032
|
+
);
|
|
1033
|
+
}
|
|
1034
|
+
for (const c of listDirCalls) {
|
|
1035
|
+
const args = c.arguments ?? {};
|
|
1036
|
+
allPromises.push(
|
|
1037
|
+
toolListDirectory(provider, args).then(
|
|
1038
|
+
(p) => formatAgentToolOutput("list_directory", args, p),
|
|
1039
|
+
(err) => formatAgentToolOutput("list_directory", args, String(err), { isError: true })
|
|
1040
|
+
)
|
|
1041
|
+
);
|
|
1042
|
+
}
|
|
1043
|
+
for (const c of readCalls) {
|
|
1044
|
+
const args = c.arguments ?? {};
|
|
1045
|
+
allPromises.push(
|
|
1046
|
+
toolRead(provider, args).then(
|
|
1047
|
+
(p) => formatAgentToolOutput("read", args, p),
|
|
1048
|
+
(err) => formatAgentToolOutput("read", args, String(err), { isError: true })
|
|
1049
|
+
)
|
|
1050
|
+
);
|
|
1051
|
+
}
|
|
1052
|
+
const toolExecStart = Date.now();
|
|
1053
|
+
const allResults = await Promise.all(allPromises);
|
|
1054
|
+
turnMetrics.local_tools_ms = Date.now() - toolExecStart;
|
|
1055
|
+
for (const result of allResults) {
|
|
1056
|
+
formatted.push(result);
|
|
1057
|
+
}
|
|
1058
|
+
if (formatted.length > 0) {
|
|
1059
|
+
const turnMessage = formatTurnMessage(turn, maxTurns);
|
|
1060
|
+
const contextBudget = calculateContextBudget(messages);
|
|
1061
|
+
messages.push({ role: "user", content: formatted.join("\n") + turnMessage + "\n" + contextBudget });
|
|
1062
|
+
}
|
|
1063
|
+
timings.turns.push(turnMetrics);
|
|
1064
|
+
if (finishCalls.length) {
|
|
1065
|
+
const fc = finishCalls[0];
|
|
1066
|
+
const files = fc.arguments?.files ?? [];
|
|
1067
|
+
const textResult = fc.arguments?.textResult;
|
|
961
1068
|
finishMeta = { files };
|
|
962
1069
|
terminationReason = "completed";
|
|
963
1070
|
if (files.length === 0) {
|
|
964
|
-
const payload2 =
|
|
965
|
-
timings.turns.push(turnMetrics);
|
|
1071
|
+
const payload2 = textResult || "No relevant code found.";
|
|
966
1072
|
timings.total_ms = Date.now() - totalStart;
|
|
967
1073
|
return {
|
|
968
1074
|
terminationReason: "completed",
|
|
@@ -971,25 +1077,8 @@ async function* runWarpGrepStreaming(config) {
|
|
|
971
1077
|
timings
|
|
972
1078
|
};
|
|
973
1079
|
}
|
|
974
|
-
timings.turns.push(turnMetrics);
|
|
975
1080
|
break;
|
|
976
1081
|
}
|
|
977
|
-
const toolExecStart = Date.now();
|
|
978
|
-
const results = await Promise.all(
|
|
979
|
-
toolCalls.map(async (tc) => {
|
|
980
|
-
const args = safeParseJSON(tc.function.arguments);
|
|
981
|
-
const output = await executeTool(provider, tc.function.name, args, repoRoot).catch((err) => String(err));
|
|
982
|
-
return { tool_call_id: tc.id, content: output };
|
|
983
|
-
})
|
|
984
|
-
);
|
|
985
|
-
turnMetrics.local_tools_ms = Date.now() - toolExecStart;
|
|
986
|
-
for (const result of results) {
|
|
987
|
-
messages.push({ role: "tool", tool_call_id: result.tool_call_id, content: result.content });
|
|
988
|
-
}
|
|
989
|
-
const turnMsg = formatTurnMessage(turn, maxTurns);
|
|
990
|
-
const budget = calculateContextBudget(messages);
|
|
991
|
-
messages.push({ role: "user", content: turnMsg + "\n" + budget });
|
|
992
|
-
timings.turns.push(turnMetrics);
|
|
993
1082
|
}
|
|
994
1083
|
if (terminationReason !== "completed" || !finishMeta) {
|
|
995
1084
|
timings.total_ms = Date.now() - totalStart;
|
|
@@ -1033,14 +1122,6 @@ async function* runWarpGrepStreaming(config) {
|
|
|
1033
1122
|
timings
|
|
1034
1123
|
};
|
|
1035
1124
|
}
|
|
1036
|
-
async function runWarpGrep(config) {
|
|
1037
|
-
const gen = runWarpGrepStreaming(config);
|
|
1038
|
-
let result = await gen.next();
|
|
1039
|
-
while (!result.done) {
|
|
1040
|
-
result = await gen.next();
|
|
1041
|
-
}
|
|
1042
|
-
return result.value;
|
|
1043
|
-
}
|
|
1044
1125
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1045
1126
|
0 && (module.exports = {
|
|
1046
1127
|
runWarpGrep,
|