@morphllm/morphsdk 0.2.56 → 0.2.57
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-SALJ2K6S.js → chunk-6X5UOY7B.js} +32 -37
- package/dist/chunk-6X5UOY7B.js.map +1 -0
- package/dist/{chunk-UIRJE422.js → chunk-CFF636UC.js} +3 -3
- package/dist/{chunk-QVRXBAMM.js → chunk-GJ5TYNRD.js} +2 -2
- package/dist/{chunk-4ZHDBKBY.js → chunk-IMYQOKFO.js} +3 -3
- package/dist/{chunk-GJURLQ3L.js → chunk-KBQWGT5L.js} +3 -3
- package/dist/{chunk-WSSSSBWU.js → chunk-QFIHUCTF.js} +5 -5
- package/dist/client.cjs +28 -142
- package/dist/client.cjs.map +1 -1
- package/dist/client.js +7 -8
- package/dist/index.cjs +28 -142
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +7 -8
- package/dist/tools/warp_grep/agent/runner.cjs +28 -142
- package/dist/tools/warp_grep/agent/runner.cjs.map +1 -1
- package/dist/tools/warp_grep/agent/runner.js +2 -3
- package/dist/tools/warp_grep/anthropic.cjs +28 -142
- package/dist/tools/warp_grep/anthropic.cjs.map +1 -1
- package/dist/tools/warp_grep/anthropic.js +4 -5
- package/dist/tools/warp_grep/harness.cjs +859 -0
- package/dist/tools/warp_grep/harness.cjs.map +1 -0
- package/dist/tools/warp_grep/harness.d.ts +176 -0
- package/dist/tools/warp_grep/harness.js +76 -0
- package/dist/tools/warp_grep/harness.js.map +1 -0
- package/dist/tools/warp_grep/index.cjs +28 -142
- package/dist/tools/warp_grep/index.cjs.map +1 -1
- package/dist/tools/warp_grep/index.js +6 -7
- package/dist/tools/warp_grep/openai.cjs +28 -142
- package/dist/tools/warp_grep/openai.cjs.map +1 -1
- package/dist/tools/warp_grep/openai.js +4 -5
- package/dist/tools/warp_grep/vercel.cjs +28 -142
- package/dist/tools/warp_grep/vercel.cjs.map +1 -1
- package/dist/tools/warp_grep/vercel.js +4 -5
- package/package.json +7 -2
- package/dist/chunk-NDZO5IPV.js +0 -121
- package/dist/chunk-NDZO5IPV.js.map +0 -1
- package/dist/chunk-SALJ2K6S.js.map +0 -1
- package/dist/tools/warp_grep/agent/grep_helpers.cjs +0 -148
- package/dist/tools/warp_grep/agent/grep_helpers.cjs.map +0 -1
- package/dist/tools/warp_grep/agent/grep_helpers.d.ts +0 -16
- package/dist/tools/warp_grep/agent/grep_helpers.js +0 -14
- package/dist/tools/warp_grep/agent/grep_helpers.js.map +0 -1
- /package/dist/{chunk-UIRJE422.js.map → chunk-CFF636UC.js.map} +0 -0
- /package/dist/{chunk-QVRXBAMM.js.map → chunk-GJ5TYNRD.js.map} +0 -0
- /package/dist/{chunk-4ZHDBKBY.js.map → chunk-IMYQOKFO.js.map} +0 -0
- /package/dist/{chunk-GJURLQ3L.js.map → chunk-KBQWGT5L.js.map} +0 -0
- /package/dist/{chunk-WSSSSBWU.js.map → chunk-QFIHUCTF.js.map} +0 -0
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
toolRead
|
|
3
3
|
} from "./chunk-HQO45BAJ.js";
|
|
4
|
+
import {
|
|
5
|
+
toolAnalyse
|
|
6
|
+
} from "./chunk-73RQWOQC.js";
|
|
4
7
|
import {
|
|
5
8
|
LLMResponseParser
|
|
6
9
|
} from "./chunk-LVPVVLTI.js";
|
|
7
10
|
import {
|
|
8
11
|
getSystemPrompt
|
|
9
12
|
} from "./chunk-WETRQJGU.js";
|
|
10
|
-
import {
|
|
11
|
-
toolAnalyse
|
|
12
|
-
} from "./chunk-73RQWOQC.js";
|
|
13
13
|
import {
|
|
14
14
|
readFinishFiles
|
|
15
15
|
} from "./chunk-EK7OQPWD.js";
|
|
@@ -20,11 +20,6 @@ import {
|
|
|
20
20
|
import {
|
|
21
21
|
formatAgentToolOutput
|
|
22
22
|
} from "./chunk-TICMYDII.js";
|
|
23
|
-
import {
|
|
24
|
-
GrepState,
|
|
25
|
-
formatTurnGrepOutput,
|
|
26
|
-
parseAndFilterGrepOutput
|
|
27
|
-
} from "./chunk-NDZO5IPV.js";
|
|
28
23
|
import {
|
|
29
24
|
fetchWithRetry,
|
|
30
25
|
withTimeout
|
|
@@ -92,7 +87,6 @@ async function runWarpGrep(config) {
|
|
|
92
87
|
const model = config.model || DEFAULT_MODEL;
|
|
93
88
|
const provider = config.provider;
|
|
94
89
|
const errors = [];
|
|
95
|
-
const grepState = new GrepState();
|
|
96
90
|
let finishMeta;
|
|
97
91
|
let terminationReason = "terminated";
|
|
98
92
|
for (let round = 1; round <= maxRounds; round += 1) {
|
|
@@ -118,10 +112,25 @@ async function runWarpGrep(config) {
|
|
|
118
112
|
const msg = c.arguments?.message || "Command skipped due to parsing error";
|
|
119
113
|
formatted.push(msg);
|
|
120
114
|
}
|
|
121
|
-
const
|
|
115
|
+
const allPromises = [];
|
|
116
|
+
for (const c of grepCalls) {
|
|
117
|
+
const args = c.arguments ?? {};
|
|
118
|
+
allPromises.push(
|
|
119
|
+
provider.grep({ pattern: args.pattern, path: args.path }).then(
|
|
120
|
+
(grepRes) => {
|
|
121
|
+
if (grepRes.error) {
|
|
122
|
+
return { terminate: true, error: grepRes.error };
|
|
123
|
+
}
|
|
124
|
+
const output = grepRes.lines.join("\n") || "no matches";
|
|
125
|
+
return formatAgentToolOutput("grep", args, output, { isError: false });
|
|
126
|
+
},
|
|
127
|
+
(err) => formatAgentToolOutput("grep", args, String(err), { isError: true })
|
|
128
|
+
)
|
|
129
|
+
);
|
|
130
|
+
}
|
|
122
131
|
for (const c of analyseCalls) {
|
|
123
132
|
const args = c.arguments ?? {};
|
|
124
|
-
|
|
133
|
+
allPromises.push(
|
|
125
134
|
toolAnalyse(provider, args).then(
|
|
126
135
|
(p) => formatAgentToolOutput("analyse", args, p, { isError: false }),
|
|
127
136
|
(err) => formatAgentToolOutput("analyse", args, String(err), { isError: true })
|
|
@@ -130,38 +139,24 @@ async function runWarpGrep(config) {
|
|
|
130
139
|
}
|
|
131
140
|
for (const c of readCalls) {
|
|
132
141
|
const args = c.arguments ?? {};
|
|
133
|
-
|
|
142
|
+
allPromises.push(
|
|
134
143
|
toolRead(provider, args).then(
|
|
135
144
|
(p) => formatAgentToolOutput("read", args, p, { isError: false }),
|
|
136
145
|
(err) => formatAgentToolOutput("read", args, String(err), { isError: true })
|
|
137
146
|
)
|
|
138
147
|
);
|
|
139
148
|
}
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
errors
|
|
148
|
-
|
|
149
|
-
return {
|
|
150
|
-
terminationReason: "terminated",
|
|
151
|
-
messages,
|
|
152
|
-
errors
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
const rawOutput = Array.isArray(grepRes.lines) ? grepRes.lines.join("\n") : "";
|
|
156
|
-
const newMatches = parseAndFilterGrepOutput(rawOutput, grepState);
|
|
157
|
-
let formattedPayload = formatTurnGrepOutput(newMatches);
|
|
158
|
-
if (formattedPayload === "No new matches found.") {
|
|
159
|
-
formattedPayload = "no new matches";
|
|
160
|
-
}
|
|
161
|
-
formatted.push(formatAgentToolOutput("grep", args, formattedPayload, { isError: false }));
|
|
162
|
-
} catch (err) {
|
|
163
|
-
formatted.push(formatAgentToolOutput("grep", args, String(err), { isError: true }));
|
|
149
|
+
const allResults = await Promise.all(allPromises);
|
|
150
|
+
for (const result of allResults) {
|
|
151
|
+
if (typeof result === "object" && "terminate" in result) {
|
|
152
|
+
errors.push({ message: result.error });
|
|
153
|
+
return {
|
|
154
|
+
terminationReason: "terminated",
|
|
155
|
+
messages,
|
|
156
|
+
errors
|
|
157
|
+
};
|
|
164
158
|
}
|
|
159
|
+
formatted.push(result);
|
|
165
160
|
}
|
|
166
161
|
if (formatted.length > 0) {
|
|
167
162
|
const turnsUsed = round;
|
|
@@ -231,4 +226,4 @@ async function runWarpGrep(config) {
|
|
|
231
226
|
export {
|
|
232
227
|
runWarpGrep
|
|
233
228
|
};
|
|
234
|
-
//# sourceMappingURL=chunk-
|
|
229
|
+
//# sourceMappingURL=chunk-6X5UOY7B.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../tools/warp_grep/agent/runner.ts"],"sourcesContent":["import { AGENT_CONFIG, DEFAULT_MODEL } from './config.js';\nimport { getSystemPrompt } from './prompt.js';\nimport type { AgentRunResult, ChatMessage, SessionConfig, ToolCall, AgentFinish } from './types.js';\nimport { LLMResponseParser } from './parser.js';\nimport type { WarpGrepProvider } from '../providers/types.js';\nimport { toolRead } from '../tools/read.js';\nimport { toolAnalyse } from '../tools/analyse.js';\nimport { fetchWithRetry, withTimeout } from '../../utils/resilience.js';\nimport { formatAgentToolOutput } from './formatter.js';\nimport { readFinishFiles } from '../tools/finish.js';\nimport path from 'path';\n\ntype EventName =\n | 'initial_state'\n | 'round_start'\n | 'round_end'\n | 'finish'\n | 'error';\n\nexport type EventCallback = (name: EventName, payload: Record<string, unknown>) => void;\n\nconst parser = new LLMResponseParser();\n\nasync function buildInitialState(repoRoot: string, query: string, provider: WarpGrepProvider): Promise<string> {\n // Summarize top-level directories and file counts using the provider\n // This works for both local and remote filesystems (Modal, E2B, etc.)\n try {\n const entries = await provider.analyse({ path: '.', maxResults: 100 });\n const dirs = entries.filter(e => e.type === 'dir').map(d => d.name).slice(0, 50);\n const files = entries.filter(e => e.type === 'file').map(f => f.name).slice(0, 50);\n const parts = [\n `<repo_root>${repoRoot}</repo_root>`,\n `<top_dirs>${dirs.join(', ')}</top_dirs>`,\n `<top_files>${files.join(', ')}</top_files>`,\n ];\n return parts.join('\\n');\n } catch {\n return `<repo_root>${repoRoot}</repo_root>`;\n }\n}\n\nfunction formatAssistantToolBlock(name: string, args: Record<string, unknown>, payload: string, isError = false): string {\n const argStr = Object.entries(args)\n .map(([k, v]) => `${k}=${JSON.stringify(v)}`)\n .join(' ');\n const prefix = isError ? 'error' : 'result';\n return `<${prefix} name=\"${name}\" ${argStr}>\\n${payload}\\n</${prefix}>`;\n}\n\nasync function callModel(messages: ChatMessage[], model: string, apiKey?: string): Promise<string> {\n const api = 'https://api.morphllm.com/v1/chat/completions';\n const fetchPromise = fetchWithRetry(\n api,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey || process.env.MORPH_API_KEY || ''}`,\n },\n body: JSON.stringify({\n model,\n temperature: 0.0,\n max_tokens: 1024,\n messages,\n }),\n },\n {}\n );\n const resp = await withTimeout(fetchPromise, AGENT_CONFIG.TIMEOUT_MS, 'morph-warp-grep request timed out');\n if (!resp.ok) {\n // keeping these cases are real throws, if this happens retry will likely not help, so best we just throw here, notice the error and fix\n const t = await resp.text();\n throw new Error(`morph-warp-grep error ${resp.status}: ${t}`);\n }\n const data = await resp.json();\n const content = data?.choices?.[0]?.message?.content;\n if (!content || typeof content !== 'string') {\n throw new Error('Invalid response from model');\n }\n return content;\n}\n\nexport async function runWarpGrep(config: SessionConfig & { provider: WarpGrepProvider }): Promise<AgentRunResult> {\n const repoRoot = path.resolve(config.repoRoot || process.cwd());\n const messages: ChatMessage[] = [];\n\n // system\n const systemMessage = { role: 'system' as const, content: getSystemPrompt() };\n messages.push(systemMessage);\n // user query\n const queryContent = `<query>${config.query}</query>`;\n messages.push({ role: 'user', content: queryContent });\n // initial state\n const initialState = await buildInitialState(repoRoot, config.query, config.provider);\n messages.push({ role: 'user', content: initialState });\n\n const maxRounds = AGENT_CONFIG.MAX_ROUNDS;\n const model = config.model || DEFAULT_MODEL;\n const provider = config.provider;\n const errors: Array<{ message: string }> = [];\n\n let finishMeta: AgentFinish | undefined;\n let terminationReason: AgentRunResult['terminationReason'] = 'terminated';\n\n for (let round = 1; round <= maxRounds; round += 1) {\n // call model\n const assistantContent = await callModel(messages, model, config.apiKey).catch((e: unknown) => {\n errors.push({ message: e instanceof Error ? e.message : String(e) });\n return '';\n });\n if (!assistantContent) break;\n messages.push({ role: 'assistant', content: assistantContent });\n\n // parse tool calls (no longer throws - returns _skip calls for malformed commands)\n const toolCalls = parser.parse(assistantContent);\n if (toolCalls.length === 0) {\n errors.push({ message: 'No tool calls produced by the model.' });\n terminationReason = 'terminated';\n break;\n }\n\n const finishCalls = toolCalls.filter(c => c.name === 'finish');\n const grepCalls = toolCalls.filter(c => c.name === 'grep');\n const analyseCalls = toolCalls.filter(c => c.name === 'analyse');\n const readCalls = toolCalls.filter(c => c.name === 'read');\n const skipCalls = toolCalls.filter(c => c.name === '_skip');\n\n const formatted: string[] = [];\n\n // Surface any skipped commands as feedback to the LLM\n for (const c of skipCalls) {\n const msg = (c.arguments as { message?: string })?.message || 'Command skipped due to parsing error';\n formatted.push(msg);\n }\n\n // Execute ALL tools in parallel (grep, analyse, read)\n const allPromises: Array<Promise<string | { terminate: true; error: string }>> = [];\n \n // Grep calls\n for (const c of grepCalls) {\n const args = (c.arguments ?? {}) as { pattern: string; path: string };\n allPromises.push(\n provider.grep({ pattern: args.pattern, path: args.path }).then(\n grepRes => {\n // Check for ripgrep availability error\n if (grepRes.error) {\n return { terminate: true, error: grepRes.error };\n }\n const output = grepRes.lines.join('\\n') || 'no matches';\n return formatAgentToolOutput('grep', args, output, { isError: false });\n },\n err => formatAgentToolOutput('grep', args, String(err), { isError: true })\n )\n );\n }\n \n // Analyse calls\n for (const c of analyseCalls) {\n const args = (c.arguments ?? {}) as { path: string; pattern?: string | null };\n allPromises.push(\n toolAnalyse(provider, args).then(\n p => formatAgentToolOutput('analyse', args, p, { isError: false }),\n err => formatAgentToolOutput('analyse', args, String(err), { isError: true })\n )\n );\n }\n \n // Read calls\n for (const c of readCalls) {\n const args = (c.arguments ?? {}) as { path: string; start?: number; end?: number };\n allPromises.push(\n toolRead(provider, args).then(\n p => formatAgentToolOutput('read', args, p, { isError: false }),\n err => formatAgentToolOutput('read', args, String(err), { isError: true })\n )\n );\n }\n \n const allResults = await Promise.all(allPromises);\n \n // Check for termination signals (e.g., ripgrep not available)\n for (const result of allResults) {\n if (typeof result === 'object' && 'terminate' in result) {\n errors.push({ message: result.error });\n return {\n terminationReason: 'terminated',\n messages,\n errors,\n };\n }\n formatted.push(result as string);\n }\n\n if (formatted.length > 0) {\n // Add turn counter message\n const turnsUsed = round;\n const turnsRemaining = 4 - turnsUsed;\n let turnMessage: string;\n if (turnsRemaining === 0) {\n turnMessage = `\\n\\n[Turn ${turnsUsed}/4] This is your LAST turn. You MUST call the finish tool now.`;\n } else if (turnsRemaining === 1) {\n turnMessage = `\\n\\n[Turn ${turnsUsed}/4] You have 1 turn remaining. Next turn you MUST call the finish tool.`;\n } else {\n turnMessage = `\\n\\n[Turn ${turnsUsed}/4] You have ${turnsRemaining} turns remaining.`;\n }\n messages.push({ role: 'user', content: formatted.join('\\n') + turnMessage });\n }\n\n if (finishCalls.length) {\n const fc = finishCalls[0];\n const files = ((fc.arguments as any)?.files ?? []) as AgentFinish['files'];\n finishMeta = { files };\n terminationReason = 'completed';\n break;\n }\n }\n\n if (terminationReason !== 'completed' || !finishMeta) {\n return { terminationReason, messages, errors };\n }\n\n // Build finish payload\n const parts: string[] = ['Relevant context found:'];\n for (const f of finishMeta.files) {\n const ranges = f.lines.map(([s, e]) => `${s}-${e}`).join(', ');\n parts.push(`- ${f.path}: ${ranges}`);\n }\n const payload = parts.join('\\n');\n\n // Resolve file contents for returned ranges\n // Wrap reader in try-catch to handle non-existent or unreadable files gracefully\n // Track files that couldn't be read for error reporting\n const fileReadErrors: Array<{ path: string; error: string }> = [];\n const resolved = await readFinishFiles(\n repoRoot,\n finishMeta.files,\n async (p: string, s: number, e: number) => {\n try {\n const rr = await provider.read({ path: p, start: s, end: e });\n // rr.lines are \"line|content\" → strip the \"line|\" prefix\n return rr.lines.map(l => {\n const idx = l.indexOf('|');\n return idx >= 0 ? l.slice(idx + 1) : l;\n });\n } catch (err) {\n // File doesn't exist or can't be read - log error but don't throw\n // This handles cases where the agent hallucinated a path or the file was deleted\n const errorMsg = err instanceof Error ? err.message : String(err);\n fileReadErrors.push({ path: p, error: errorMsg });\n console.error(`[warp_grep] Failed to read file: ${p} - ${errorMsg}`);\n return [`[couldn't find: ${p}]`];\n }\n }\n );\n\n // Add file read errors to the result so MCP can report them\n if (fileReadErrors.length > 0) {\n errors.push(...fileReadErrors.map(e => ({ message: `File read error: ${e.path} - ${e.error}` })));\n }\n\n return {\n terminationReason: 'completed',\n messages,\n finish: { payload, metadata: finishMeta, resolved },\n };\n}\n\n\n"],"mappings":"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","names":[]}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
WARP_GREP_DESCRIPTION
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-GJ5TYNRD.js";
|
|
4
4
|
import {
|
|
5
5
|
runWarpGrep
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-6X5UOY7B.js";
|
|
7
7
|
import {
|
|
8
8
|
LocalRipgrepProvider
|
|
9
9
|
} from "./chunk-ZJIIICRA.js";
|
|
@@ -67,4 +67,4 @@ export {
|
|
|
67
67
|
execute,
|
|
68
68
|
createMorphWarpGrepTool
|
|
69
69
|
};
|
|
70
|
-
//# sourceMappingURL=chunk-
|
|
70
|
+
//# sourceMappingURL=chunk-CFF636UC.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
runWarpGrep
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-6X5UOY7B.js";
|
|
4
4
|
import {
|
|
5
5
|
LocalRipgrepProvider
|
|
6
6
|
} from "./chunk-ZJIIICRA.js";
|
|
@@ -104,4 +104,4 @@ export {
|
|
|
104
104
|
WARP_GREP_TOOL_NAME,
|
|
105
105
|
WARP_GREP_DESCRIPTION
|
|
106
106
|
};
|
|
107
|
-
//# sourceMappingURL=chunk-
|
|
107
|
+
//# sourceMappingURL=chunk-GJ5TYNRD.js.map
|
|
@@ -2,10 +2,10 @@ import {
|
|
|
2
2
|
WARP_GREP_DESCRIPTION,
|
|
3
3
|
WARP_GREP_TOOL_NAME,
|
|
4
4
|
formatResult
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-GJ5TYNRD.js";
|
|
6
6
|
import {
|
|
7
7
|
runWarpGrep
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-6X5UOY7B.js";
|
|
9
9
|
import {
|
|
10
10
|
getSystemPrompt
|
|
11
11
|
} from "./chunk-WETRQJGU.js";
|
|
@@ -80,4 +80,4 @@ export {
|
|
|
80
80
|
createMorphWarpGrepTool,
|
|
81
81
|
openai_default
|
|
82
82
|
};
|
|
83
|
-
//# sourceMappingURL=chunk-
|
|
83
|
+
//# sourceMappingURL=chunk-IMYQOKFO.js.map
|
|
@@ -2,10 +2,10 @@ import {
|
|
|
2
2
|
WARP_GREP_DESCRIPTION,
|
|
3
3
|
WARP_GREP_TOOL_NAME,
|
|
4
4
|
formatResult
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-GJ5TYNRD.js";
|
|
6
6
|
import {
|
|
7
7
|
runWarpGrep
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-6X5UOY7B.js";
|
|
9
9
|
import {
|
|
10
10
|
getSystemPrompt
|
|
11
11
|
} from "./chunk-WETRQJGU.js";
|
|
@@ -74,4 +74,4 @@ export {
|
|
|
74
74
|
createMorphWarpGrepTool,
|
|
75
75
|
anthropic_default
|
|
76
76
|
};
|
|
77
|
-
//# sourceMappingURL=chunk-
|
|
77
|
+
//# sourceMappingURL=chunk-KBQWGT5L.js.map
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createMorphWarpGrepTool as createMorphWarpGrepTool2
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-KBQWGT5L.js";
|
|
4
4
|
import {
|
|
5
5
|
createMorphWarpGrepTool
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-IMYQOKFO.js";
|
|
7
7
|
import {
|
|
8
8
|
createMorphWarpGrepTool as createMorphWarpGrepTool3
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-CFF636UC.js";
|
|
10
10
|
import {
|
|
11
11
|
WarpGrepClient
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-GJ5TYNRD.js";
|
|
13
13
|
import {
|
|
14
14
|
createCodebaseSearchTool as createCodebaseSearchTool3
|
|
15
15
|
} from "./chunk-UBX7QYBD.js";
|
|
@@ -280,4 +280,4 @@ export {
|
|
|
280
280
|
VercelToolFactory,
|
|
281
281
|
MorphClient
|
|
282
282
|
};
|
|
283
|
-
//# sourceMappingURL=chunk-
|
|
283
|
+
//# sourceMappingURL=chunk-QFIHUCTF.js.map
|
package/dist/client.cjs
CHANGED
|
@@ -1446,120 +1446,6 @@ function formatAgentToolOutput(toolName, args, output, options = {}) {
|
|
|
1446
1446
|
return sharedFormatter.format(toolName, args, output, options);
|
|
1447
1447
|
}
|
|
1448
1448
|
|
|
1449
|
-
// tools/warp_grep/agent/grep_helpers.ts
|
|
1450
|
-
var GrepState = class {
|
|
1451
|
-
seenLines = /* @__PURE__ */ new Set();
|
|
1452
|
-
isNew(path4, lineNumber) {
|
|
1453
|
-
const key = this.makeKey(path4, lineNumber);
|
|
1454
|
-
return !this.seenLines.has(key);
|
|
1455
|
-
}
|
|
1456
|
-
add(path4, lineNumber) {
|
|
1457
|
-
this.seenLines.add(this.makeKey(path4, lineNumber));
|
|
1458
|
-
}
|
|
1459
|
-
makeKey(path4, lineNumber) {
|
|
1460
|
-
return `${path4}:${lineNumber}`;
|
|
1461
|
-
}
|
|
1462
|
-
};
|
|
1463
|
-
var MAX_GREP_OUTPUT_CHARS_PER_TURN = 6e4;
|
|
1464
|
-
function extractMatchFields(payload) {
|
|
1465
|
-
const text = payload.replace(/\r?\n$/, "");
|
|
1466
|
-
if (!text || text.startsWith("[error]")) {
|
|
1467
|
-
return null;
|
|
1468
|
-
}
|
|
1469
|
-
const firstSep = text.indexOf(":");
|
|
1470
|
-
if (firstSep === -1) {
|
|
1471
|
-
return null;
|
|
1472
|
-
}
|
|
1473
|
-
let filePath = text.slice(0, firstSep).trim();
|
|
1474
|
-
if (!filePath) {
|
|
1475
|
-
return null;
|
|
1476
|
-
}
|
|
1477
|
-
if (filePath.startsWith("./") || filePath.startsWith(".\\")) {
|
|
1478
|
-
filePath = filePath.slice(2);
|
|
1479
|
-
}
|
|
1480
|
-
const remainder = text.slice(firstSep + 1);
|
|
1481
|
-
const secondSep = remainder.indexOf(":");
|
|
1482
|
-
if (secondSep === -1) {
|
|
1483
|
-
return null;
|
|
1484
|
-
}
|
|
1485
|
-
const linePart = remainder.slice(0, secondSep);
|
|
1486
|
-
const lineNumber = Number.parseInt(linePart, 10);
|
|
1487
|
-
if (!Number.isInteger(lineNumber) || lineNumber <= 0) {
|
|
1488
|
-
return null;
|
|
1489
|
-
}
|
|
1490
|
-
let contentSegment = remainder.slice(secondSep + 1);
|
|
1491
|
-
const columnSep = contentSegment.indexOf(":");
|
|
1492
|
-
if (columnSep !== -1 && /^\d+$/.test(contentSegment.slice(0, columnSep))) {
|
|
1493
|
-
contentSegment = contentSegment.slice(columnSep + 1);
|
|
1494
|
-
}
|
|
1495
|
-
const content = contentSegment.trim();
|
|
1496
|
-
if (!content) {
|
|
1497
|
-
return null;
|
|
1498
|
-
}
|
|
1499
|
-
return { path: filePath, lineNumber, content };
|
|
1500
|
-
}
|
|
1501
|
-
function parseAndFilterGrepOutput(rawOutput, state) {
|
|
1502
|
-
const matches = [];
|
|
1503
|
-
if (typeof rawOutput !== "string" || !rawOutput.trim()) {
|
|
1504
|
-
return matches;
|
|
1505
|
-
}
|
|
1506
|
-
for (const line of rawOutput.split(/\r?\n/)) {
|
|
1507
|
-
const fields = extractMatchFields(line);
|
|
1508
|
-
if (!fields) {
|
|
1509
|
-
continue;
|
|
1510
|
-
}
|
|
1511
|
-
if (state.isNew(fields.path, fields.lineNumber)) {
|
|
1512
|
-
matches.push(fields);
|
|
1513
|
-
state.add(fields.path, fields.lineNumber);
|
|
1514
|
-
}
|
|
1515
|
-
}
|
|
1516
|
-
return matches;
|
|
1517
|
-
}
|
|
1518
|
-
function truncateOutput(payload, maxChars) {
|
|
1519
|
-
if (payload.length <= maxChars) {
|
|
1520
|
-
return payload;
|
|
1521
|
-
}
|
|
1522
|
-
const note = "... (output truncated)";
|
|
1523
|
-
const available = maxChars - note.length - 1;
|
|
1524
|
-
if (available <= 0) {
|
|
1525
|
-
return note;
|
|
1526
|
-
}
|
|
1527
|
-
if (payload.length <= available) {
|
|
1528
|
-
return `${payload.slice(0, available).replace(/\n$/, "")}
|
|
1529
|
-
${note}`;
|
|
1530
|
-
}
|
|
1531
|
-
const core = payload.slice(0, Math.max(0, available - 1));
|
|
1532
|
-
const trimmed = core.replace(/\n$/, "").replace(/\s+$/, "");
|
|
1533
|
-
const snippet = trimmed ? `${trimmed}\u2026` : "\u2026";
|
|
1534
|
-
return `${snippet}
|
|
1535
|
-
${note}`;
|
|
1536
|
-
}
|
|
1537
|
-
function formatTurnGrepOutput(matches, maxChars = MAX_GREP_OUTPUT_CHARS_PER_TURN) {
|
|
1538
|
-
if (!matches || matches.length === 0) {
|
|
1539
|
-
return "No new matches found.";
|
|
1540
|
-
}
|
|
1541
|
-
const matchesByFile = /* @__PURE__ */ new Map();
|
|
1542
|
-
for (const match of matches) {
|
|
1543
|
-
if (!matchesByFile.has(match.path)) {
|
|
1544
|
-
matchesByFile.set(match.path, []);
|
|
1545
|
-
}
|
|
1546
|
-
matchesByFile.get(match.path).push(match);
|
|
1547
|
-
}
|
|
1548
|
-
const lines = [];
|
|
1549
|
-
const sortedPaths = Array.from(matchesByFile.keys()).sort();
|
|
1550
|
-
sortedPaths.forEach((filePath, index) => {
|
|
1551
|
-
if (index > 0) {
|
|
1552
|
-
lines.push("");
|
|
1553
|
-
}
|
|
1554
|
-
lines.push(filePath);
|
|
1555
|
-
const sortedMatches = matchesByFile.get(filePath).slice().sort((a, b) => a.lineNumber - b.lineNumber);
|
|
1556
|
-
for (const match of sortedMatches) {
|
|
1557
|
-
lines.push(`${match.lineNumber}:${match.content}`);
|
|
1558
|
-
}
|
|
1559
|
-
});
|
|
1560
|
-
return truncateOutput(lines.join("\n"), maxChars);
|
|
1561
|
-
}
|
|
1562
|
-
|
|
1563
1449
|
// tools/warp_grep/tools/finish.ts
|
|
1564
1450
|
async function readFinishFiles(repoRoot, files, reader) {
|
|
1565
1451
|
const out = [];
|
|
@@ -1655,7 +1541,6 @@ async function runWarpGrep(config) {
|
|
|
1655
1541
|
const model = config.model || DEFAULT_MODEL;
|
|
1656
1542
|
const provider = config.provider;
|
|
1657
1543
|
const errors = [];
|
|
1658
|
-
const grepState = new GrepState();
|
|
1659
1544
|
let finishMeta;
|
|
1660
1545
|
let terminationReason = "terminated";
|
|
1661
1546
|
for (let round = 1; round <= maxRounds; round += 1) {
|
|
@@ -1681,10 +1566,25 @@ async function runWarpGrep(config) {
|
|
|
1681
1566
|
const msg = c.arguments?.message || "Command skipped due to parsing error";
|
|
1682
1567
|
formatted.push(msg);
|
|
1683
1568
|
}
|
|
1684
|
-
const
|
|
1569
|
+
const allPromises = [];
|
|
1570
|
+
for (const c of grepCalls) {
|
|
1571
|
+
const args = c.arguments ?? {};
|
|
1572
|
+
allPromises.push(
|
|
1573
|
+
provider.grep({ pattern: args.pattern, path: args.path }).then(
|
|
1574
|
+
(grepRes) => {
|
|
1575
|
+
if (grepRes.error) {
|
|
1576
|
+
return { terminate: true, error: grepRes.error };
|
|
1577
|
+
}
|
|
1578
|
+
const output = grepRes.lines.join("\n") || "no matches";
|
|
1579
|
+
return formatAgentToolOutput("grep", args, output, { isError: false });
|
|
1580
|
+
},
|
|
1581
|
+
(err) => formatAgentToolOutput("grep", args, String(err), { isError: true })
|
|
1582
|
+
)
|
|
1583
|
+
);
|
|
1584
|
+
}
|
|
1685
1585
|
for (const c of analyseCalls) {
|
|
1686
1586
|
const args = c.arguments ?? {};
|
|
1687
|
-
|
|
1587
|
+
allPromises.push(
|
|
1688
1588
|
toolAnalyse(provider, args).then(
|
|
1689
1589
|
(p) => formatAgentToolOutput("analyse", args, p, { isError: false }),
|
|
1690
1590
|
(err) => formatAgentToolOutput("analyse", args, String(err), { isError: true })
|
|
@@ -1693,38 +1593,24 @@ async function runWarpGrep(config) {
|
|
|
1693
1593
|
}
|
|
1694
1594
|
for (const c of readCalls) {
|
|
1695
1595
|
const args = c.arguments ?? {};
|
|
1696
|
-
|
|
1596
|
+
allPromises.push(
|
|
1697
1597
|
toolRead(provider, args).then(
|
|
1698
1598
|
(p) => formatAgentToolOutput("read", args, p, { isError: false }),
|
|
1699
1599
|
(err) => formatAgentToolOutput("read", args, String(err), { isError: true })
|
|
1700
1600
|
)
|
|
1701
1601
|
);
|
|
1702
1602
|
}
|
|
1703
|
-
const
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
errors
|
|
1711
|
-
|
|
1712
|
-
return {
|
|
1713
|
-
terminationReason: "terminated",
|
|
1714
|
-
messages,
|
|
1715
|
-
errors
|
|
1716
|
-
};
|
|
1717
|
-
}
|
|
1718
|
-
const rawOutput = Array.isArray(grepRes.lines) ? grepRes.lines.join("\n") : "";
|
|
1719
|
-
const newMatches = parseAndFilterGrepOutput(rawOutput, grepState);
|
|
1720
|
-
let formattedPayload = formatTurnGrepOutput(newMatches);
|
|
1721
|
-
if (formattedPayload === "No new matches found.") {
|
|
1722
|
-
formattedPayload = "no new matches";
|
|
1723
|
-
}
|
|
1724
|
-
formatted.push(formatAgentToolOutput("grep", args, formattedPayload, { isError: false }));
|
|
1725
|
-
} catch (err) {
|
|
1726
|
-
formatted.push(formatAgentToolOutput("grep", args, String(err), { isError: true }));
|
|
1603
|
+
const allResults = await Promise.all(allPromises);
|
|
1604
|
+
for (const result of allResults) {
|
|
1605
|
+
if (typeof result === "object" && "terminate" in result) {
|
|
1606
|
+
errors.push({ message: result.error });
|
|
1607
|
+
return {
|
|
1608
|
+
terminationReason: "terminated",
|
|
1609
|
+
messages,
|
|
1610
|
+
errors
|
|
1611
|
+
};
|
|
1727
1612
|
}
|
|
1613
|
+
formatted.push(result);
|
|
1728
1614
|
}
|
|
1729
1615
|
if (formatted.length > 0) {
|
|
1730
1616
|
const turnsUsed = round;
|