@morphllm/morphsdk 0.2.114 → 0.2.116
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-HI35Y6EZ.js → chunk-3JVHMOYJ.js} +18 -5
- package/dist/chunk-3JVHMOYJ.js.map +1 -0
- package/dist/{chunk-ALTKGCG5.js → chunk-5JARN2NG.js} +2 -2
- package/dist/{chunk-3U7AWFBN.js → chunk-62OVBE6G.js} +5 -5
- package/dist/{chunk-FL4ZBHK2.js → chunk-GENFEPHG.js} +2 -2
- package/dist/{chunk-23R562QJ.js → chunk-OVNPKTEG.js} +9 -4
- package/dist/chunk-OVNPKTEG.js.map +1 -0
- package/dist/{chunk-YY7NZLAI.js → chunk-RBOCP2MX.js} +42 -20
- package/dist/chunk-RBOCP2MX.js.map +1 -0
- package/dist/{client-Bm_umdno.d.ts → client-D7iO2TbA.d.ts} +7 -0
- package/dist/client.cjs +61 -23
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.js +6 -6
- package/dist/index.cjs +61 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +6 -6
- package/dist/tools/warp_grep/agent/runner.cjs +41 -19
- package/dist/tools/warp_grep/agent/runner.cjs.map +1 -1
- package/dist/tools/warp_grep/agent/runner.js +1 -1
- package/dist/tools/warp_grep/anthropic.cjs +39 -19
- package/dist/tools/warp_grep/anthropic.cjs.map +1 -1
- package/dist/tools/warp_grep/anthropic.js +3 -3
- package/dist/tools/warp_grep/client.cjs +49 -21
- package/dist/tools/warp_grep/client.cjs.map +1 -1
- package/dist/tools/warp_grep/client.d.ts +7 -1
- package/dist/tools/warp_grep/client.js +4 -2
- package/dist/tools/warp_grep/gemini.cjs +39 -19
- package/dist/tools/warp_grep/gemini.cjs.map +1 -1
- package/dist/tools/warp_grep/gemini.js +2 -2
- package/dist/tools/warp_grep/index.cjs +47 -21
- package/dist/tools/warp_grep/index.cjs.map +1 -1
- package/dist/tools/warp_grep/index.js +2 -2
- package/dist/tools/warp_grep/openai.cjs +39 -19
- package/dist/tools/warp_grep/openai.cjs.map +1 -1
- package/dist/tools/warp_grep/openai.js +3 -3
- package/dist/tools/warp_grep/vercel.cjs +247 -21
- package/dist/tools/warp_grep/vercel.cjs.map +1 -1
- package/dist/tools/warp_grep/vercel.d.ts +7 -0
- package/dist/tools/warp_grep/vercel.js +3 -3
- package/package.json +6 -1
- package/dist/chunk-23R562QJ.js.map +0 -1
- package/dist/chunk-HI35Y6EZ.js.map +0 -1
- package/dist/chunk-YY7NZLAI.js.map +0 -1
- /package/dist/{chunk-ALTKGCG5.js.map → chunk-5JARN2NG.js.map} +0 -0
- /package/dist/{chunk-3U7AWFBN.js.map → chunk-62OVBE6G.js.map} +0 -0
- /package/dist/{chunk-FL4ZBHK2.js.map → chunk-GENFEPHG.js.map} +0 -0
|
@@ -3,12 +3,12 @@ import {
|
|
|
3
3
|
execute,
|
|
4
4
|
openai_default,
|
|
5
5
|
warpGrepTool
|
|
6
|
-
} from "../../chunk-
|
|
6
|
+
} from "../../chunk-GENFEPHG.js";
|
|
7
7
|
import "../../chunk-KW7OEGZK.js";
|
|
8
8
|
import {
|
|
9
9
|
formatResult
|
|
10
|
-
} from "../../chunk-
|
|
11
|
-
import "../../chunk-
|
|
10
|
+
} from "../../chunk-OVNPKTEG.js";
|
|
11
|
+
import "../../chunk-RBOCP2MX.js";
|
|
12
12
|
import "../../chunk-PUGSTXLO.js";
|
|
13
13
|
import "../../chunk-3MLWXJTJ.js";
|
|
14
14
|
import "../../chunk-SNGGSPYJ.js";
|
|
@@ -1370,27 +1370,41 @@ async function callModel(messages, model, options = {}) {
|
|
|
1370
1370
|
maxRetries: options.retryConfig?.maxRetries,
|
|
1371
1371
|
timeout: timeoutMs
|
|
1372
1372
|
});
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
data
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1373
|
+
const MAX_EMPTY_RETRIES = 1;
|
|
1374
|
+
for (let attempt = 0; attempt <= MAX_EMPTY_RETRIES; attempt++) {
|
|
1375
|
+
let data;
|
|
1376
|
+
try {
|
|
1377
|
+
data = await client.chat.completions.create({
|
|
1378
|
+
model,
|
|
1379
|
+
temperature: 0,
|
|
1380
|
+
max_tokens: 1024,
|
|
1381
|
+
messages
|
|
1382
|
+
});
|
|
1383
|
+
} catch (error) {
|
|
1384
|
+
if (error instanceof import_openai.default.APIError && error.status === 404) {
|
|
1385
|
+
throw new Error(
|
|
1386
|
+
"The endpoint you are trying to call is likely deprecated. Please update with: npm cache clean --force && npx -y @morphllm/morphmcp@latest or visit: https://morphllm.com/mcp"
|
|
1387
|
+
);
|
|
1388
|
+
}
|
|
1389
|
+
throw error;
|
|
1390
|
+
}
|
|
1391
|
+
const choice = data?.choices?.[0];
|
|
1392
|
+
const content = choice?.message?.content;
|
|
1393
|
+
if (content && typeof content === "string") {
|
|
1394
|
+
return content;
|
|
1395
|
+
}
|
|
1396
|
+
if (attempt === MAX_EMPTY_RETRIES) {
|
|
1397
|
+
const finishReason = choice?.finish_reason ?? "unknown";
|
|
1398
|
+
const hasToolCalls = Array.isArray(choice?.message?.tool_calls) && choice.message.tool_calls.length > 0;
|
|
1399
|
+
const choicesLen = data?.choices?.length ?? 0;
|
|
1400
|
+
const contentType = content === null ? "null" : content === void 0 ? "undefined" : typeof content;
|
|
1383
1401
|
throw new Error(
|
|
1384
|
-
|
|
1402
|
+
`Invalid response from model: content=${contentType}, finish_reason=${finishReason}, has_tool_calls=${hasToolCalls}, choices_length=${choicesLen}`
|
|
1385
1403
|
);
|
|
1386
1404
|
}
|
|
1387
|
-
|
|
1388
|
-
}
|
|
1389
|
-
const content = data?.choices?.[0]?.message?.content;
|
|
1390
|
-
if (!content || typeof content !== "string") {
|
|
1391
|
-
throw new Error("Invalid response from model");
|
|
1405
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
1392
1406
|
}
|
|
1393
|
-
|
|
1407
|
+
throw new Error("Invalid response from model");
|
|
1394
1408
|
}
|
|
1395
1409
|
async function runWarpGrep(config) {
|
|
1396
1410
|
const totalStart = Date.now();
|
|
@@ -1419,22 +1433,180 @@ async function runWarpGrep(config) {
|
|
|
1419
1433
|
retryConfig: config.retryConfig,
|
|
1420
1434
|
timeout: timeoutMs
|
|
1421
1435
|
}).catch((e) => {
|
|
1422
|
-
|
|
1436
|
+
const errMsg = e instanceof Error ? e.message : String(e);
|
|
1437
|
+
console.error(`[warp_grep] Morph API call failed on turn ${turn}:`, errMsg);
|
|
1438
|
+
errors.push({ message: errMsg });
|
|
1439
|
+
return "";
|
|
1440
|
+
});
|
|
1441
|
+
turnMetrics.morph_api_ms = Date.now() - modelCallStart;
|
|
1442
|
+
if (!assistantContent) {
|
|
1443
|
+
console.error(`[warp_grep] Empty response from Morph API on turn ${turn}. Errors so far:`, errors);
|
|
1444
|
+
timings.turns.push(turnMetrics);
|
|
1445
|
+
break;
|
|
1446
|
+
}
|
|
1447
|
+
messages.push({ role: "assistant", content: assistantContent });
|
|
1448
|
+
const toolCalls = parser.parse(assistantContent);
|
|
1449
|
+
if (toolCalls.length === 0) {
|
|
1450
|
+
console.error(`[warp_grep] No tool calls parsed on turn ${turn}. Assistant content (first 500 chars):`, assistantContent.slice(0, 500));
|
|
1451
|
+
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" });
|
|
1452
|
+
terminationReason = "terminated";
|
|
1453
|
+
timings.turns.push(turnMetrics);
|
|
1454
|
+
break;
|
|
1455
|
+
}
|
|
1456
|
+
const finishCalls = toolCalls.filter((c) => c.name === "finish");
|
|
1457
|
+
const grepCalls = toolCalls.filter((c) => c.name === "grep");
|
|
1458
|
+
const listDirCalls = toolCalls.filter((c) => c.name === "list_directory");
|
|
1459
|
+
const readCalls = toolCalls.filter((c) => c.name === "read");
|
|
1460
|
+
const skipCalls = toolCalls.filter((c) => c.name === "_skip");
|
|
1461
|
+
const formatted = [];
|
|
1462
|
+
for (const c of skipCalls) {
|
|
1463
|
+
const msg = c.arguments?.message || "Command skipped due to parsing error";
|
|
1464
|
+
formatted.push(msg);
|
|
1465
|
+
}
|
|
1466
|
+
const allPromises = [];
|
|
1467
|
+
for (const c of grepCalls) {
|
|
1468
|
+
const args = c.arguments ?? {};
|
|
1469
|
+
allPromises.push(
|
|
1470
|
+
toolGrep(provider, args).then(
|
|
1471
|
+
({ output }) => formatAgentToolOutput("grep", args, output, { isError: false }),
|
|
1472
|
+
(err) => formatAgentToolOutput("grep", args, String(err), { isError: true })
|
|
1473
|
+
)
|
|
1474
|
+
);
|
|
1475
|
+
}
|
|
1476
|
+
for (const c of listDirCalls) {
|
|
1477
|
+
const args = c.arguments ?? {};
|
|
1478
|
+
allPromises.push(
|
|
1479
|
+
toolListDirectory(provider, args).then(
|
|
1480
|
+
(p) => formatAgentToolOutput("list_directory", args, p, { isError: false }),
|
|
1481
|
+
(err) => formatAgentToolOutput("list_directory", args, String(err), { isError: true })
|
|
1482
|
+
)
|
|
1483
|
+
);
|
|
1484
|
+
}
|
|
1485
|
+
for (const c of readCalls) {
|
|
1486
|
+
const args = c.arguments ?? {};
|
|
1487
|
+
allPromises.push(
|
|
1488
|
+
toolRead(provider, args).then(
|
|
1489
|
+
(p) => formatAgentToolOutput("read", args, p, { isError: false }),
|
|
1490
|
+
(err) => formatAgentToolOutput("read", args, String(err), { isError: true })
|
|
1491
|
+
)
|
|
1492
|
+
);
|
|
1493
|
+
}
|
|
1494
|
+
const toolExecStart = Date.now();
|
|
1495
|
+
const allResults = await Promise.all(allPromises);
|
|
1496
|
+
turnMetrics.local_tools_ms = Date.now() - toolExecStart;
|
|
1497
|
+
for (const result of allResults) {
|
|
1498
|
+
formatted.push(result);
|
|
1499
|
+
}
|
|
1500
|
+
if (formatted.length > 0) {
|
|
1501
|
+
const turnMessage = formatTurnMessage(turn, maxTurns);
|
|
1502
|
+
const contextBudget = calculateContextBudget(messages);
|
|
1503
|
+
messages.push({ role: "user", content: formatted.join("\n") + turnMessage + "\n" + contextBudget });
|
|
1504
|
+
}
|
|
1505
|
+
timings.turns.push(turnMetrics);
|
|
1506
|
+
if (finishCalls.length) {
|
|
1507
|
+
const fc = finishCalls[0];
|
|
1508
|
+
const files = fc.arguments?.files ?? [];
|
|
1509
|
+
finishMeta = { files };
|
|
1510
|
+
terminationReason = "completed";
|
|
1511
|
+
break;
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
if (terminationReason !== "completed" || !finishMeta) {
|
|
1515
|
+
timings.total_ms = Date.now() - totalStart;
|
|
1516
|
+
return { terminationReason, messages, errors, timings };
|
|
1517
|
+
}
|
|
1518
|
+
const parts = ["Relevant context found:"];
|
|
1519
|
+
for (const f of finishMeta.files) {
|
|
1520
|
+
const ranges = f.lines === "*" ? "*" : Array.isArray(f.lines) ? f.lines.map(([s, e]) => `${s}-${e}`).join(", ") : "*";
|
|
1521
|
+
parts.push(`- ${f.path}: ${ranges}`);
|
|
1522
|
+
}
|
|
1523
|
+
const payload = parts.join("\n");
|
|
1524
|
+
const finishResolutionStart = Date.now();
|
|
1525
|
+
const fileReadErrors = [];
|
|
1526
|
+
const resolved = await readFinishFiles(
|
|
1527
|
+
repoRoot,
|
|
1528
|
+
finishMeta.files,
|
|
1529
|
+
async (p, s, e) => {
|
|
1530
|
+
try {
|
|
1531
|
+
const rr = await provider.read({ path: p, start: s, end: e });
|
|
1532
|
+
return rr.lines.map((l) => {
|
|
1533
|
+
const idx = l.indexOf("|");
|
|
1534
|
+
return idx >= 0 ? l.slice(idx + 1) : l;
|
|
1535
|
+
});
|
|
1536
|
+
} catch (err) {
|
|
1537
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
1538
|
+
fileReadErrors.push({ path: p, error: errorMsg });
|
|
1539
|
+
console.error(`[warp_grep] Failed to read file: ${p} - ${errorMsg}`);
|
|
1540
|
+
return [`[couldn't find: ${p}]`];
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
);
|
|
1544
|
+
timings.finish_resolution_ms = Date.now() - finishResolutionStart;
|
|
1545
|
+
if (fileReadErrors.length > 0) {
|
|
1546
|
+
errors.push(...fileReadErrors.map((e) => ({ message: `File read error: ${e.path} - ${e.error}` })));
|
|
1547
|
+
}
|
|
1548
|
+
timings.total_ms = Date.now() - totalStart;
|
|
1549
|
+
return {
|
|
1550
|
+
terminationReason: "completed",
|
|
1551
|
+
messages,
|
|
1552
|
+
finish: { payload, metadata: finishMeta, resolved },
|
|
1553
|
+
timings
|
|
1554
|
+
};
|
|
1555
|
+
}
|
|
1556
|
+
async function* runWarpGrepStreaming(config) {
|
|
1557
|
+
const totalStart = Date.now();
|
|
1558
|
+
const timeoutMs = config.timeout ?? AGENT_CONFIG.TIMEOUT_MS;
|
|
1559
|
+
const timings = { turns: [], timeout_ms: timeoutMs };
|
|
1560
|
+
const repoRoot = import_path2.default.resolve(config.repoRoot || process.cwd());
|
|
1561
|
+
const messages = [];
|
|
1562
|
+
messages.push({ role: "system", content: getSystemPrompt() });
|
|
1563
|
+
const initialStateStart = Date.now();
|
|
1564
|
+
const initialState = await buildInitialState(repoRoot, config.query, config.provider);
|
|
1565
|
+
timings.initial_state_ms = Date.now() - initialStateStart;
|
|
1566
|
+
messages.push({ role: "user", content: initialState });
|
|
1567
|
+
const maxTurns = AGENT_CONFIG.MAX_TURNS;
|
|
1568
|
+
const model = config.model || DEFAULT_MODEL;
|
|
1569
|
+
const provider = config.provider;
|
|
1570
|
+
const errors = [];
|
|
1571
|
+
let finishMeta;
|
|
1572
|
+
let terminationReason = "terminated";
|
|
1573
|
+
for (let turn = 1; turn <= maxTurns; turn += 1) {
|
|
1574
|
+
const turnMetrics = { turn, morph_api_ms: 0, local_tools_ms: 0 };
|
|
1575
|
+
enforceContextLimit(messages);
|
|
1576
|
+
const modelCallStart = Date.now();
|
|
1577
|
+
const assistantContent = await callModel(messages, model, {
|
|
1578
|
+
morphApiKey: config.morphApiKey,
|
|
1579
|
+
morphApiUrl: config.morphApiUrl,
|
|
1580
|
+
retryConfig: config.retryConfig,
|
|
1581
|
+
timeout: timeoutMs
|
|
1582
|
+
}).catch((e) => {
|
|
1583
|
+
const errMsg = e instanceof Error ? e.message : String(e);
|
|
1584
|
+
console.error(`[warp_grep:stream] Morph API call failed on turn ${turn}:`, errMsg);
|
|
1585
|
+
errors.push({ message: errMsg });
|
|
1423
1586
|
return "";
|
|
1424
1587
|
});
|
|
1425
1588
|
turnMetrics.morph_api_ms = Date.now() - modelCallStart;
|
|
1426
1589
|
if (!assistantContent) {
|
|
1590
|
+
console.error(`[warp_grep:stream] Empty response from Morph API on turn ${turn}. Errors so far:`, errors);
|
|
1427
1591
|
timings.turns.push(turnMetrics);
|
|
1428
1592
|
break;
|
|
1429
1593
|
}
|
|
1430
1594
|
messages.push({ role: "assistant", content: assistantContent });
|
|
1431
1595
|
const toolCalls = parser.parse(assistantContent);
|
|
1432
1596
|
if (toolCalls.length === 0) {
|
|
1597
|
+
console.error(`[warp_grep:stream] No tool calls parsed on turn ${turn}. Assistant content (first 500 chars):`, assistantContent.slice(0, 500));
|
|
1433
1598
|
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" });
|
|
1434
1599
|
terminationReason = "terminated";
|
|
1435
1600
|
timings.turns.push(turnMetrics);
|
|
1436
1601
|
break;
|
|
1437
1602
|
}
|
|
1603
|
+
yield {
|
|
1604
|
+
turn,
|
|
1605
|
+
toolCalls: toolCalls.map((c) => ({
|
|
1606
|
+
name: c.name,
|
|
1607
|
+
arguments: c.arguments ?? {}
|
|
1608
|
+
}))
|
|
1609
|
+
};
|
|
1438
1610
|
const finishCalls = toolCalls.filter((c) => c.name === "finish");
|
|
1439
1611
|
const grepCalls = toolCalls.filter((c) => c.name === "grep");
|
|
1440
1612
|
const listDirCalls = toolCalls.filter((c) => c.name === "list_directory");
|
|
@@ -1739,7 +1911,9 @@ async function executeToolCall(input, config) {
|
|
|
1739
1911
|
});
|
|
1740
1912
|
const finish = result.finish;
|
|
1741
1913
|
if (result.terminationReason !== "completed" || !finish?.metadata) {
|
|
1742
|
-
|
|
1914
|
+
const errorDetails = result.errors?.map((e) => e.message).join("; ") || "unknown reason";
|
|
1915
|
+
console.error(`[warp_grep] executeToolCall failed. Reason: ${result.terminationReason}. Errors: ${errorDetails}. Turns: ${result.timings?.turns?.length ?? 0}`);
|
|
1916
|
+
return { success: false, error: `Search did not complete: ${errorDetails}` };
|
|
1743
1917
|
}
|
|
1744
1918
|
const contexts = (finish.resolved ?? []).map((r) => ({
|
|
1745
1919
|
file: r.path,
|
|
@@ -1748,6 +1922,46 @@ async function executeToolCall(input, config) {
|
|
|
1748
1922
|
}));
|
|
1749
1923
|
return { success: true, contexts, summary: finish.payload };
|
|
1750
1924
|
}
|
|
1925
|
+
function processAgentResult(result) {
|
|
1926
|
+
const finish = result.finish;
|
|
1927
|
+
if (result.terminationReason !== "completed" || !finish?.metadata) {
|
|
1928
|
+
const errorDetails = result.errors?.map((e) => e.message).join("; ") || "unknown reason";
|
|
1929
|
+
console.error(`[warp_grep] processAgentResult failed. Reason: ${result.terminationReason}. Errors: ${errorDetails}. Turns: ${result.timings?.turns?.length ?? 0}`);
|
|
1930
|
+
return { success: false, error: `Search did not complete: ${errorDetails}` };
|
|
1931
|
+
}
|
|
1932
|
+
const contexts = (finish.resolved ?? []).map((r) => ({
|
|
1933
|
+
file: r.path,
|
|
1934
|
+
content: r.content,
|
|
1935
|
+
lines: r.ranges
|
|
1936
|
+
}));
|
|
1937
|
+
return { success: true, contexts, summary: finish.payload };
|
|
1938
|
+
}
|
|
1939
|
+
async function* executeToolCallStreaming(input, config) {
|
|
1940
|
+
const parsed = typeof input === "string" ? JSON.parse(input) : input;
|
|
1941
|
+
const provider = config.remoteCommands ? new RemoteCommandsProvider(config.repoRoot, config.remoteCommands) : config.provider ?? await getLocalProvider(config.repoRoot, config.excludes);
|
|
1942
|
+
const generator = runWarpGrepStreaming({
|
|
1943
|
+
query: parsed.query,
|
|
1944
|
+
repoRoot: config.repoRoot,
|
|
1945
|
+
provider,
|
|
1946
|
+
excludes: config.excludes,
|
|
1947
|
+
includes: config.includes,
|
|
1948
|
+
debug: config.debug ?? false,
|
|
1949
|
+
morphApiKey: config.morphApiKey,
|
|
1950
|
+
morphApiUrl: config.morphApiUrl,
|
|
1951
|
+
retryConfig: config.retryConfig,
|
|
1952
|
+
timeout: config.timeout
|
|
1953
|
+
});
|
|
1954
|
+
let agentResult;
|
|
1955
|
+
for (; ; ) {
|
|
1956
|
+
const { value, done } = await generator.next();
|
|
1957
|
+
if (done) {
|
|
1958
|
+
agentResult = value;
|
|
1959
|
+
break;
|
|
1960
|
+
}
|
|
1961
|
+
yield value;
|
|
1962
|
+
}
|
|
1963
|
+
return processAgentResult(agentResult);
|
|
1964
|
+
}
|
|
1751
1965
|
function formatResult(result) {
|
|
1752
1966
|
if (!result.success) {
|
|
1753
1967
|
return `Search failed: ${result.error}`;
|
|
@@ -1799,14 +2013,26 @@ function createWarpGrepTool(config) {
|
|
|
1799
2013
|
description: config.description ?? WARP_GREP_DESCRIPTION,
|
|
1800
2014
|
inputSchema: schema,
|
|
1801
2015
|
execute: async (params) => {
|
|
1802
|
-
const
|
|
2016
|
+
const steps = [];
|
|
2017
|
+
const generator = executeToolCallStreaming(params, config);
|
|
2018
|
+
let result;
|
|
2019
|
+
for (; ; ) {
|
|
2020
|
+
const { value, done } = await generator.next();
|
|
2021
|
+
if (done) {
|
|
2022
|
+
result = value;
|
|
2023
|
+
break;
|
|
2024
|
+
}
|
|
2025
|
+
steps.push(value);
|
|
2026
|
+
}
|
|
1803
2027
|
if (!result.success) {
|
|
1804
2028
|
throw new Error(`Failed to search codebase: ${result.error}`);
|
|
1805
2029
|
}
|
|
2030
|
+
const allToolCalls = steps.flatMap((s) => s.toolCalls);
|
|
1806
2031
|
return {
|
|
1807
2032
|
success: true,
|
|
1808
2033
|
contexts: result.contexts,
|
|
1809
|
-
summary: result.summary
|
|
2034
|
+
summary: result.summary,
|
|
2035
|
+
progress: allToolCalls.length > 0 ? { turn: steps.length, toolCalls: allToolCalls } : void 0
|
|
1810
2036
|
};
|
|
1811
2037
|
}
|
|
1812
2038
|
});
|