ak-gemini 2.0.7 → 2.1.1
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/GUIDE.md +16 -0
- package/README.md +1 -0
- package/code-agent.js +435 -173
- package/index.cjs +524 -178
- package/package.json +1 -1
- package/tool-agent.js +133 -59
- package/types.d.ts +47 -23
package/index.cjs
CHANGED
|
@@ -1325,6 +1325,24 @@ var Message = class extends base_default {
|
|
|
1325
1325
|
var message_default = Message;
|
|
1326
1326
|
|
|
1327
1327
|
// tool-agent.js
|
|
1328
|
+
async function runWithConcurrency(tasks, concurrency) {
|
|
1329
|
+
if (concurrency === Infinity) return Promise.all(tasks.map((t) => t()));
|
|
1330
|
+
if (concurrency === 1) {
|
|
1331
|
+
const results2 = [];
|
|
1332
|
+
for (const t of tasks) results2.push(await t());
|
|
1333
|
+
return results2;
|
|
1334
|
+
}
|
|
1335
|
+
const results = new Array(tasks.length);
|
|
1336
|
+
let next = 0;
|
|
1337
|
+
async function worker() {
|
|
1338
|
+
while (next < tasks.length) {
|
|
1339
|
+
const i = next++;
|
|
1340
|
+
results[i] = await tasks[i]();
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, tasks.length) }, () => worker()));
|
|
1344
|
+
return results;
|
|
1345
|
+
}
|
|
1328
1346
|
var ToolAgent = class extends base_default {
|
|
1329
1347
|
/**
|
|
1330
1348
|
* @param {ToolAgentOptions} [options={}]
|
|
@@ -1342,6 +1360,8 @@ var ToolAgent = class extends base_default {
|
|
|
1342
1360
|
if (this.toolExecutor && this.tools.length === 0) {
|
|
1343
1361
|
throw new Error("ToolAgent: toolExecutor provided without tools. Provide tool declarations so the model knows what tools are available.");
|
|
1344
1362
|
}
|
|
1363
|
+
this.parallelToolCalls = options.parallelToolCalls ?? true;
|
|
1364
|
+
this._concurrency = this.parallelToolCalls === true ? Infinity : this.parallelToolCalls === false ? 1 : this.parallelToolCalls;
|
|
1345
1365
|
this.maxToolRounds = options.maxToolRounds || 10;
|
|
1346
1366
|
this.onToolCall = options.onToolCall || null;
|
|
1347
1367
|
this.onBeforeExecution = options.onBeforeExecution || null;
|
|
@@ -1372,38 +1392,36 @@ var ToolAgent = class extends base_default {
|
|
|
1372
1392
|
if (this._stopped) break;
|
|
1373
1393
|
const functionCalls = response.functionCalls;
|
|
1374
1394
|
if (!functionCalls || functionCalls.length === 0) break;
|
|
1375
|
-
const
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
logger_default.warn(`onToolCall callback error: ${e.message}`);
|
|
1382
|
-
}
|
|
1383
|
-
}
|
|
1384
|
-
if (this.onBeforeExecution) {
|
|
1385
|
-
try {
|
|
1386
|
-
const allowed = await this.onBeforeExecution(call.name, call.args);
|
|
1387
|
-
if (allowed === false) {
|
|
1388
|
-
const result2 = { error: "Execution denied by onBeforeExecution callback" };
|
|
1389
|
-
allToolCalls.push({ name: call.name, args: call.args, result: result2 });
|
|
1390
|
-
return { id: call.id, name: call.name, result: result2 };
|
|
1391
|
-
}
|
|
1392
|
-
} catch (e) {
|
|
1393
|
-
logger_default.warn(`onBeforeExecution callback error: ${e.message}`);
|
|
1394
|
-
}
|
|
1395
|
+
const tasks = functionCalls.map((call) => async () => {
|
|
1396
|
+
if (this.onToolCall) {
|
|
1397
|
+
try {
|
|
1398
|
+
this.onToolCall(call.name, call.args);
|
|
1399
|
+
} catch (e) {
|
|
1400
|
+
logger_default.warn(`onToolCall callback error: ${e.message}`);
|
|
1395
1401
|
}
|
|
1396
|
-
|
|
1402
|
+
}
|
|
1403
|
+
if (this.onBeforeExecution) {
|
|
1397
1404
|
try {
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1405
|
+
const allowed = await this.onBeforeExecution(call.name, call.args);
|
|
1406
|
+
if (allowed === false) {
|
|
1407
|
+
const result2 = { error: "Execution denied by onBeforeExecution callback" };
|
|
1408
|
+
return { id: call.id, name: call.name, args: call.args, result: result2 };
|
|
1409
|
+
}
|
|
1410
|
+
} catch (e) {
|
|
1411
|
+
logger_default.warn(`onBeforeExecution callback error: ${e.message}`);
|
|
1402
1412
|
}
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1413
|
+
}
|
|
1414
|
+
let result;
|
|
1415
|
+
try {
|
|
1416
|
+
result = await this.toolExecutor(call.name, call.args);
|
|
1417
|
+
} catch (err) {
|
|
1418
|
+
logger_default.warn(`Tool ${call.name} failed: ${err.message}`);
|
|
1419
|
+
result = { error: err.message };
|
|
1420
|
+
}
|
|
1421
|
+
return { id: call.id, name: call.name, args: call.args, result };
|
|
1422
|
+
});
|
|
1423
|
+
const toolResults = await runWithConcurrency(tasks, this._concurrency);
|
|
1424
|
+
for (const r of toolResults) allToolCalls.push({ name: r.name, args: r.args, result: r.result });
|
|
1407
1425
|
response = await this._withRetry(() => this.chatSession.sendMessage({
|
|
1408
1426
|
message: toolResults.map((r) => ({
|
|
1409
1427
|
functionResponse: {
|
|
@@ -1471,39 +1489,81 @@ var ToolAgent = class extends base_default {
|
|
|
1471
1489
|
return;
|
|
1472
1490
|
}
|
|
1473
1491
|
const toolResults = [];
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1492
|
+
if (this._concurrency === 1) {
|
|
1493
|
+
for (const call of functionCalls) {
|
|
1494
|
+
if (this._stopped) break;
|
|
1495
|
+
yield { type: "tool_call", toolName: call.name, args: call.args };
|
|
1496
|
+
if (this.onToolCall) {
|
|
1497
|
+
try {
|
|
1498
|
+
this.onToolCall(call.name, call.args);
|
|
1499
|
+
} catch (e) {
|
|
1500
|
+
logger_default.warn(`onToolCall callback error: ${e.message}`);
|
|
1501
|
+
}
|
|
1482
1502
|
}
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1503
|
+
let denied = false;
|
|
1504
|
+
if (this.onBeforeExecution) {
|
|
1505
|
+
try {
|
|
1506
|
+
const allowed = await this.onBeforeExecution(call.name, call.args);
|
|
1507
|
+
if (allowed === false) denied = true;
|
|
1508
|
+
} catch (e) {
|
|
1509
|
+
logger_default.warn(`onBeforeExecution callback error: ${e.message}`);
|
|
1510
|
+
}
|
|
1491
1511
|
}
|
|
1512
|
+
let result;
|
|
1513
|
+
if (denied) {
|
|
1514
|
+
result = { error: "Execution denied by onBeforeExecution callback" };
|
|
1515
|
+
} else {
|
|
1516
|
+
try {
|
|
1517
|
+
result = await this.toolExecutor(call.name, call.args);
|
|
1518
|
+
} catch (err) {
|
|
1519
|
+
logger_default.warn(`Tool ${call.name} failed: ${err.message}`);
|
|
1520
|
+
result = { error: err.message };
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
allToolCalls.push({ name: call.name, args: call.args, result });
|
|
1524
|
+
yield { type: "tool_result", toolName: call.name, result };
|
|
1525
|
+
toolResults.push({ id: call.id, name: call.name, result });
|
|
1492
1526
|
}
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
}
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1527
|
+
} else {
|
|
1528
|
+
for (const call of functionCalls) {
|
|
1529
|
+
yield { type: "tool_call", toolName: call.name, args: call.args };
|
|
1530
|
+
}
|
|
1531
|
+
const tasks = functionCalls.map((call) => async () => {
|
|
1532
|
+
if (this.onToolCall) {
|
|
1533
|
+
try {
|
|
1534
|
+
this.onToolCall(call.name, call.args);
|
|
1535
|
+
} catch (e) {
|
|
1536
|
+
logger_default.warn(`onToolCall callback error: ${e.message}`);
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
let denied = false;
|
|
1540
|
+
if (this.onBeforeExecution) {
|
|
1541
|
+
try {
|
|
1542
|
+
const allowed = await this.onBeforeExecution(call.name, call.args);
|
|
1543
|
+
if (allowed === false) denied = true;
|
|
1544
|
+
} catch (e) {
|
|
1545
|
+
logger_default.warn(`onBeforeExecution callback error: ${e.message}`);
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
let result;
|
|
1549
|
+
if (denied) {
|
|
1550
|
+
result = { error: "Execution denied by onBeforeExecution callback" };
|
|
1551
|
+
} else {
|
|
1552
|
+
try {
|
|
1553
|
+
result = await this.toolExecutor(call.name, call.args);
|
|
1554
|
+
} catch (err) {
|
|
1555
|
+
logger_default.warn(`Tool ${call.name} failed: ${err.message}`);
|
|
1556
|
+
result = { error: err.message };
|
|
1557
|
+
}
|
|
1502
1558
|
}
|
|
1559
|
+
return { id: call.id, name: call.name, args: call.args, result };
|
|
1560
|
+
});
|
|
1561
|
+
const results = await runWithConcurrency(tasks, this._concurrency);
|
|
1562
|
+
for (const r of results) {
|
|
1563
|
+
allToolCalls.push({ name: r.name, args: r.args, result: r.result });
|
|
1564
|
+
yield { type: "tool_result", toolName: r.name, result: r.result };
|
|
1565
|
+
toolResults.push({ id: r.id, name: r.name, result: r.result });
|
|
1503
1566
|
}
|
|
1504
|
-
allToolCalls.push({ name: call.name, args: call.args, result });
|
|
1505
|
-
yield { type: "tool_result", toolName: call.name, result };
|
|
1506
|
-
toolResults.push({ id: call.id, name: call.name, result });
|
|
1507
1567
|
}
|
|
1508
1568
|
streamResponse = await this._withRetry(() => this.chatSession.sendMessageStream({
|
|
1509
1569
|
message: toolResults.map((r) => ({
|
|
@@ -1543,6 +1603,7 @@ var import_node_crypto = require("node:crypto");
|
|
|
1543
1603
|
var MAX_OUTPUT_CHARS = 5e4;
|
|
1544
1604
|
var MAX_FILE_TREE_LINES = 500;
|
|
1545
1605
|
var IGNORE_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "coverage", ".next", "build", "__pycache__"]);
|
|
1606
|
+
var EXECUTING_TOOLS = /* @__PURE__ */ new Set(["execute_code", "write_and_run_code", "run_bash"]);
|
|
1546
1607
|
var CodeAgent = class extends base_default {
|
|
1547
1608
|
/**
|
|
1548
1609
|
* @param {CodeAgentOptions} [options={}]
|
|
@@ -1562,53 +1623,146 @@ var CodeAgent = class extends base_default {
|
|
|
1562
1623
|
this.keepArtifacts = options.keepArtifacts ?? false;
|
|
1563
1624
|
this.comments = options.comments ?? false;
|
|
1564
1625
|
this.maxRetries = options.maxRetries ?? 3;
|
|
1626
|
+
this.skills = options.skills || [];
|
|
1627
|
+
this.envOverview = options.envOverview || "";
|
|
1565
1628
|
this._codebaseContext = null;
|
|
1566
1629
|
this._contextGathered = false;
|
|
1567
1630
|
this._stopped = false;
|
|
1568
1631
|
this._activeProcess = null;
|
|
1569
1632
|
this._userSystemPrompt = options.systemPrompt || "";
|
|
1570
1633
|
this._allExecutions = [];
|
|
1571
|
-
this.
|
|
1572
|
-
|
|
1634
|
+
this._skillRegistry = /* @__PURE__ */ new Map();
|
|
1635
|
+
this.chatConfig.tools = [this._buildToolDefinitions()];
|
|
1636
|
+
this.chatConfig.toolConfig = { functionCallingConfig: { mode: "AUTO" } };
|
|
1637
|
+
logger_default.debug(`CodeAgent created for directory: ${this.workingDirectory}`);
|
|
1638
|
+
}
|
|
1639
|
+
// ── Tool Definitions ─────────────────────────────────────────────────────
|
|
1640
|
+
/**
|
|
1641
|
+
* Build tool definitions in Gemini format.
|
|
1642
|
+
* use_skill is only included when skills are registered.
|
|
1643
|
+
* @private
|
|
1644
|
+
* @returns {{ functionDeclarations: Array<Object> }}
|
|
1645
|
+
*/
|
|
1646
|
+
_buildToolDefinitions() {
|
|
1647
|
+
const declarations = [
|
|
1648
|
+
{
|
|
1649
|
+
name: "write_code",
|
|
1650
|
+
description: "Output code without executing it. Use this when you want to show, propose, or present code to the user without running it.",
|
|
1651
|
+
parametersJsonSchema: {
|
|
1652
|
+
type: "object",
|
|
1653
|
+
properties: {
|
|
1654
|
+
code: { type: "string", description: "The code to output." },
|
|
1655
|
+
purpose: { type: "string", description: 'A short 2-4 word slug describing the code (e.g., "api-client", "data-parser").' },
|
|
1656
|
+
language: { type: "string", description: 'Programming language of the code (default: "javascript").' }
|
|
1657
|
+
},
|
|
1658
|
+
required: ["code"]
|
|
1659
|
+
}
|
|
1660
|
+
},
|
|
1661
|
+
{
|
|
1573
1662
|
name: "execute_code",
|
|
1574
|
-
description: "Execute JavaScript code in a Node.js child process.
|
|
1663
|
+
description: "Execute a given piece of JavaScript code in a Node.js child process. Use this when you already have code to run \u2014 e.g., running code from a previous write_code call, re-running a snippet, or executing code the user provided. Use console.log() for output.",
|
|
1575
1664
|
parametersJsonSchema: {
|
|
1576
1665
|
type: "object",
|
|
1577
1666
|
properties: {
|
|
1578
|
-
code: {
|
|
1579
|
-
|
|
1580
|
-
description: "JavaScript code to execute. Use console.log() for output. You can import any built-in Node.js module."
|
|
1581
|
-
},
|
|
1582
|
-
purpose: {
|
|
1583
|
-
type: "string",
|
|
1584
|
-
description: 'A short 2-4 word slug describing what this script does (e.g., "read-config", "parse-logs", "fetch-api-data"). Used for naming the script file.'
|
|
1585
|
-
}
|
|
1667
|
+
code: { type: "string", description: "JavaScript code to execute. Use console.log() for output. Use import syntax (ES modules)." },
|
|
1668
|
+
purpose: { type: "string", description: 'A short 2-4 word slug describing what this script does (e.g., "read-config", "parse-logs").' }
|
|
1586
1669
|
},
|
|
1587
1670
|
required: ["code"]
|
|
1588
1671
|
}
|
|
1589
|
-
}
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1672
|
+
},
|
|
1673
|
+
{
|
|
1674
|
+
name: "write_and_run_code",
|
|
1675
|
+
description: "Write a fresh solution from scratch and execute it in one step. Use this when you need to figure out the code AND run it \u2014 the autonomous, end-to-end tool for solving problems with code.",
|
|
1676
|
+
parametersJsonSchema: {
|
|
1677
|
+
type: "object",
|
|
1678
|
+
properties: {
|
|
1679
|
+
code: { type: "string", description: "JavaScript code to write and execute. Use console.log() for output. Use import syntax (ES modules)." },
|
|
1680
|
+
purpose: { type: "string", description: 'A short 2-4 word slug describing what this script does (e.g., "fetch-api-data", "generate-report").' }
|
|
1681
|
+
},
|
|
1682
|
+
required: ["code"]
|
|
1683
|
+
}
|
|
1684
|
+
},
|
|
1685
|
+
{
|
|
1686
|
+
name: "fix_code",
|
|
1687
|
+
description: "Fix broken code. Provide the original and fixed versions with an explanation. Optionally execute the fix to verify it works.",
|
|
1688
|
+
parametersJsonSchema: {
|
|
1689
|
+
type: "object",
|
|
1690
|
+
properties: {
|
|
1691
|
+
original_code: { type: "string", description: "The original broken code." },
|
|
1692
|
+
fixed_code: { type: "string", description: "The corrected code." },
|
|
1693
|
+
explanation: { type: "string", description: "Brief explanation of what was wrong and how it was fixed." },
|
|
1694
|
+
execute: { type: "boolean", description: "If true, execute the fixed code to verify it works (default: false)." }
|
|
1695
|
+
},
|
|
1696
|
+
required: ["original_code", "fixed_code"]
|
|
1697
|
+
}
|
|
1698
|
+
},
|
|
1699
|
+
{
|
|
1700
|
+
name: "run_bash",
|
|
1701
|
+
description: "Execute a shell command in the working directory. Use this for file operations, git commands, installing packages, or any shell task. Prefer this over execute_code for simple shell operations.",
|
|
1702
|
+
parametersJsonSchema: {
|
|
1703
|
+
type: "object",
|
|
1704
|
+
properties: {
|
|
1705
|
+
command: { type: "string", description: "The shell command to execute." },
|
|
1706
|
+
purpose: { type: "string", description: 'A short 2-4 word slug describing the command (e.g., "list-files", "install-deps").' }
|
|
1707
|
+
},
|
|
1708
|
+
required: ["command"]
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
];
|
|
1712
|
+
if (this._skillRegistry && this._skillRegistry.size > 0) {
|
|
1713
|
+
declarations.push({
|
|
1714
|
+
name: "use_skill",
|
|
1715
|
+
description: `Load a skill by name to get instructions, templates, or patterns. Available skills: ${[...this._skillRegistry.keys()].join(", ")}`,
|
|
1716
|
+
parametersJsonSchema: {
|
|
1717
|
+
type: "object",
|
|
1718
|
+
properties: {
|
|
1719
|
+
skill_name: { type: "string", description: "The name of the skill to load." }
|
|
1720
|
+
},
|
|
1721
|
+
required: ["skill_name"]
|
|
1722
|
+
}
|
|
1723
|
+
});
|
|
1724
|
+
}
|
|
1725
|
+
return { functionDeclarations: declarations };
|
|
1593
1726
|
}
|
|
1594
1727
|
// ── Init ─────────────────────────────────────────────────────────────────
|
|
1595
1728
|
/**
|
|
1596
|
-
* Initialize the agent: gather codebase context, build system prompt
|
|
1597
|
-
* and create the chat session.
|
|
1729
|
+
* Initialize the agent: load skills, gather codebase context, and build system prompt.
|
|
1598
1730
|
* @param {boolean} [force=false]
|
|
1599
1731
|
*/
|
|
1600
1732
|
async init(force = false) {
|
|
1601
1733
|
if (this.chatSession && !force) return;
|
|
1734
|
+
if (this.skills.length > 0 && (this._skillRegistry.size === 0 || force)) {
|
|
1735
|
+
await this._loadSkills();
|
|
1736
|
+
}
|
|
1737
|
+
this.chatConfig.tools = [this._buildToolDefinitions()];
|
|
1602
1738
|
if (!this._contextGathered || force) {
|
|
1603
1739
|
await this._gatherCodebaseContext();
|
|
1604
1740
|
}
|
|
1605
|
-
|
|
1606
|
-
this.chatConfig.systemInstruction = systemPrompt;
|
|
1741
|
+
this.chatConfig.systemInstruction = this._buildSystemPrompt();
|
|
1607
1742
|
await super.init(force);
|
|
1608
1743
|
}
|
|
1744
|
+
// ── Skill Loading ────────────────────────────────────────────────────────
|
|
1745
|
+
/**
|
|
1746
|
+
* Load skill files into the skill registry.
|
|
1747
|
+
* @private
|
|
1748
|
+
*/
|
|
1749
|
+
async _loadSkills() {
|
|
1750
|
+
this._skillRegistry.clear();
|
|
1751
|
+
for (const filePath of this.skills) {
|
|
1752
|
+
try {
|
|
1753
|
+
const content = await (0, import_promises2.readFile)(filePath, "utf-8");
|
|
1754
|
+
let name = (0, import_node_path.basename)(filePath).replace(/\.md$/i, "");
|
|
1755
|
+
const fmMatch = content.match(/^---\s*\n[\s\S]*?^name:\s*(.+)$/m);
|
|
1756
|
+
if (fmMatch) name = fmMatch[1].trim();
|
|
1757
|
+
this._skillRegistry.set(name, { name, content, path: filePath });
|
|
1758
|
+
logger_default.debug(`Loaded skill: ${name} from ${filePath}`);
|
|
1759
|
+
} catch (e) {
|
|
1760
|
+
logger_default.warn(`skills: could not load "${filePath}": ${e.message}`);
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1609
1764
|
// ── Context Gathering ────────────────────────────────────────────────────
|
|
1610
1765
|
/**
|
|
1611
|
-
* Gather file tree and key file contents from the working directory.
|
|
1612
1766
|
* @private
|
|
1613
1767
|
*/
|
|
1614
1768
|
async _gatherCodebaseContext() {
|
|
@@ -1657,12 +1811,7 @@ var CodeAgent = class extends base_default {
|
|
|
1657
1811
|
this._contextGathered = true;
|
|
1658
1812
|
}
|
|
1659
1813
|
/**
|
|
1660
|
-
* Resolve an importantFiles entry against the file tree.
|
|
1661
|
-
* Supports exact matches and partial (basename/suffix) matches.
|
|
1662
1814
|
* @private
|
|
1663
|
-
* @param {string} filename
|
|
1664
|
-
* @param {string[]} fileTreeLines
|
|
1665
|
-
* @returns {string|null}
|
|
1666
1815
|
*/
|
|
1667
1816
|
_resolveImportantFile(filename, fileTreeLines) {
|
|
1668
1817
|
const exact = fileTreeLines.find((line) => line === filename);
|
|
@@ -1673,9 +1822,7 @@ var CodeAgent = class extends base_default {
|
|
|
1673
1822
|
return partial || null;
|
|
1674
1823
|
}
|
|
1675
1824
|
/**
|
|
1676
|
-
* Get file tree using git ls-files.
|
|
1677
1825
|
* @private
|
|
1678
|
-
* @returns {Promise<string>}
|
|
1679
1826
|
*/
|
|
1680
1827
|
async _getFileTreeGit() {
|
|
1681
1828
|
return new Promise((resolve2, reject) => {
|
|
@@ -1690,12 +1837,7 @@ var CodeAgent = class extends base_default {
|
|
|
1690
1837
|
});
|
|
1691
1838
|
}
|
|
1692
1839
|
/**
|
|
1693
|
-
* Fallback file tree via recursive readdir.
|
|
1694
1840
|
* @private
|
|
1695
|
-
* @param {string} dir
|
|
1696
|
-
* @param {number} depth
|
|
1697
|
-
* @param {number} maxDepth
|
|
1698
|
-
* @returns {Promise<string>}
|
|
1699
1841
|
*/
|
|
1700
1842
|
async _getFileTreeReaddir(dir, depth, maxDepth) {
|
|
1701
1843
|
if (depth >= maxDepth) return "";
|
|
@@ -1719,17 +1861,39 @@ var CodeAgent = class extends base_default {
|
|
|
1719
1861
|
return entries.join("\n");
|
|
1720
1862
|
}
|
|
1721
1863
|
/**
|
|
1722
|
-
* Build the full system prompt with codebase context.
|
|
1723
1864
|
* @private
|
|
1724
|
-
* @returns {string}
|
|
1725
1865
|
*/
|
|
1726
1866
|
_buildSystemPrompt() {
|
|
1727
1867
|
const { fileTree, npmPackages, importantFileContents } = this._codebaseContext || { fileTree: "", npmPackages: [], importantFileContents: [] };
|
|
1728
1868
|
let prompt = `You are a coding agent working in ${this.workingDirectory}.
|
|
1729
1869
|
|
|
1730
|
-
##
|
|
1731
|
-
|
|
1732
|
-
|
|
1870
|
+
## Available Tools
|
|
1871
|
+
|
|
1872
|
+
### write_code
|
|
1873
|
+
Output code without executing it. Use when showing, proposing, or presenting code to the user.
|
|
1874
|
+
|
|
1875
|
+
### execute_code
|
|
1876
|
+
Run a given piece of JavaScript code. Use when you already have code to run \u2014 e.g., from a previous write_code call, re-running a snippet, or executing user-provided code.
|
|
1877
|
+
|
|
1878
|
+
### write_and_run_code
|
|
1879
|
+
Write a fresh solution from scratch and execute it in one step. The autonomous, end-to-end tool for solving problems with code.
|
|
1880
|
+
|
|
1881
|
+
### fix_code
|
|
1882
|
+
Fix broken code by providing original and fixed versions. Set execute=true to verify the fix works.
|
|
1883
|
+
|
|
1884
|
+
### run_bash
|
|
1885
|
+
Run shell commands directly (e.g., ls, grep, curl, git, npm, cat). Prefer this over execute_code for simple shell operations.`;
|
|
1886
|
+
if (this._skillRegistry.size > 0) {
|
|
1887
|
+
prompt += `
|
|
1888
|
+
|
|
1889
|
+
### use_skill
|
|
1890
|
+
Load a skill by name to get detailed instructions and templates. Available skills: ${[...this._skillRegistry.keys()].join(", ")}`;
|
|
1891
|
+
}
|
|
1892
|
+
prompt += `
|
|
1893
|
+
|
|
1894
|
+
## Code Execution Rules
|
|
1895
|
+
These rules apply when using execute_code, write_and_run_code, or fix_code (with execute=true):
|
|
1896
|
+
- Always provide a short descriptive \`purpose\` parameter (2-4 word slug like "read-config")
|
|
1733
1897
|
- Your code runs in a Node.js child process with access to all built-in modules
|
|
1734
1898
|
- IMPORTANT: Your code runs as an ES module (.mjs). Use import syntax, NOT require():
|
|
1735
1899
|
- import fs from 'fs';
|
|
@@ -1737,9 +1901,7 @@ var CodeAgent = class extends base_default {
|
|
|
1737
1901
|
- import { execSync } from 'child_process';
|
|
1738
1902
|
- Use console.log() to produce output \u2014 that's how results are returned to you
|
|
1739
1903
|
- Write efficient scripts that do multiple things per execution when possible
|
|
1740
|
-
- For parallel async operations, use Promise.all()
|
|
1741
|
-
const [a, b] = await Promise.all([fetchA(), fetchB()]);
|
|
1742
|
-
- Read files with fs.readFileSync() when you need to understand their contents
|
|
1904
|
+
- For parallel async operations, use Promise.all()
|
|
1743
1905
|
- Handle errors in your scripts with try/catch so you get useful error messages
|
|
1744
1906
|
- Top-level await is supported
|
|
1745
1907
|
- The working directory is: ${this.workingDirectory}`;
|
|
@@ -1783,34 +1945,33 @@ ${content}
|
|
|
1783
1945
|
|
|
1784
1946
|
## Additional Instructions
|
|
1785
1947
|
${this._userSystemPrompt}`;
|
|
1948
|
+
}
|
|
1949
|
+
if (this.envOverview) {
|
|
1950
|
+
prompt += `
|
|
1951
|
+
|
|
1952
|
+
## Environment Overview
|
|
1953
|
+
${this.envOverview}`;
|
|
1786
1954
|
}
|
|
1787
1955
|
return prompt;
|
|
1788
1956
|
}
|
|
1789
1957
|
// ── Code Execution ───────────────────────────────────────────────────────
|
|
1790
1958
|
/**
|
|
1791
|
-
* Generate a sanitized slug from a purpose string.
|
|
1792
1959
|
* @private
|
|
1793
|
-
* @param {string} [purpose]
|
|
1794
|
-
* @returns {string}
|
|
1795
1960
|
*/
|
|
1796
1961
|
_slugify(purpose) {
|
|
1797
1962
|
if (!purpose) return (0, import_node_crypto.randomUUID)().slice(0, 8);
|
|
1798
1963
|
return purpose.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
|
|
1799
1964
|
}
|
|
1800
1965
|
/**
|
|
1801
|
-
* Execute a JavaScript code string in a child process.
|
|
1802
1966
|
* @private
|
|
1803
|
-
* @param {string} code - JavaScript code to execute
|
|
1804
|
-
* @param {string} [purpose] - Short description for file naming
|
|
1805
|
-
* @returns {Promise<{stdout: string, stderr: string, exitCode: number, denied?: boolean}>}
|
|
1806
1967
|
*/
|
|
1807
|
-
async _executeCode(code, purpose) {
|
|
1968
|
+
async _executeCode(code, purpose, toolName) {
|
|
1808
1969
|
if (this._stopped) {
|
|
1809
1970
|
return { stdout: "", stderr: "Agent was stopped", exitCode: -1 };
|
|
1810
1971
|
}
|
|
1811
1972
|
if (this.onBeforeExecution) {
|
|
1812
1973
|
try {
|
|
1813
|
-
const allowed = await this.onBeforeExecution(code);
|
|
1974
|
+
const allowed = await this.onBeforeExecution(code, toolName || "execute_code");
|
|
1814
1975
|
if (allowed === false) {
|
|
1815
1976
|
return { stdout: "", stderr: "Execution denied by onBeforeExecution callback", exitCode: -1, denied: true };
|
|
1816
1977
|
}
|
|
@@ -1859,7 +2020,8 @@ ${this._userSystemPrompt}`;
|
|
|
1859
2020
|
output: result.stdout,
|
|
1860
2021
|
stderr: result.stderr,
|
|
1861
2022
|
exitCode: result.exitCode,
|
|
1862
|
-
filePath: this.keepArtifacts ? tempFile : null
|
|
2023
|
+
filePath: this.keepArtifacts ? tempFile : null,
|
|
2024
|
+
tool: toolName || "execute_code"
|
|
1863
2025
|
});
|
|
1864
2026
|
if (this.onCodeExecution) {
|
|
1865
2027
|
try {
|
|
@@ -1878,11 +2040,75 @@ ${this._userSystemPrompt}`;
|
|
|
1878
2040
|
}
|
|
1879
2041
|
}
|
|
1880
2042
|
}
|
|
2043
|
+
// ── Bash Execution ───────────────────────────────────────────────────────
|
|
2044
|
+
/**
|
|
2045
|
+
* Execute a bash command in the working directory.
|
|
2046
|
+
* @private
|
|
2047
|
+
*/
|
|
2048
|
+
async _executeBash(command, purpose) {
|
|
2049
|
+
if (this._stopped) {
|
|
2050
|
+
return { stdout: "", stderr: "Agent was stopped", exitCode: -1 };
|
|
2051
|
+
}
|
|
2052
|
+
if (this.onBeforeExecution) {
|
|
2053
|
+
try {
|
|
2054
|
+
const allowed = await this.onBeforeExecution(command, "run_bash");
|
|
2055
|
+
if (allowed === false) {
|
|
2056
|
+
return { stdout: "", stderr: "Execution denied by onBeforeExecution callback", exitCode: -1, denied: true };
|
|
2057
|
+
}
|
|
2058
|
+
} catch (e) {
|
|
2059
|
+
logger_default.warn(`onBeforeExecution callback error: ${e.message}`);
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
const result = await new Promise((resolve2) => {
|
|
2063
|
+
const child = (0, import_node_child_process.execFile)("bash", ["-c", command], {
|
|
2064
|
+
cwd: this.workingDirectory,
|
|
2065
|
+
timeout: this.timeout,
|
|
2066
|
+
env: process.env,
|
|
2067
|
+
maxBuffer: 10 * 1024 * 1024
|
|
2068
|
+
}, (err, stdout, stderr) => {
|
|
2069
|
+
this._activeProcess = null;
|
|
2070
|
+
if (err) {
|
|
2071
|
+
resolve2({
|
|
2072
|
+
stdout: err.stdout || stdout || "",
|
|
2073
|
+
stderr: (err.stderr || stderr || "") + (err.killed ? "\n[EXECUTION TIMED OUT]" : ""),
|
|
2074
|
+
exitCode: err.code || 1
|
|
2075
|
+
});
|
|
2076
|
+
} else {
|
|
2077
|
+
resolve2({ stdout: stdout || "", stderr: stderr || "", exitCode: 0 });
|
|
2078
|
+
}
|
|
2079
|
+
});
|
|
2080
|
+
this._activeProcess = child;
|
|
2081
|
+
});
|
|
2082
|
+
const totalLen = result.stdout.length + result.stderr.length;
|
|
2083
|
+
if (totalLen > MAX_OUTPUT_CHARS) {
|
|
2084
|
+
const half = Math.floor(MAX_OUTPUT_CHARS / 2);
|
|
2085
|
+
if (result.stdout.length > half) {
|
|
2086
|
+
result.stdout = result.stdout.slice(0, half) + "\n...[OUTPUT TRUNCATED]";
|
|
2087
|
+
}
|
|
2088
|
+
if (result.stderr.length > half) {
|
|
2089
|
+
result.stderr = result.stderr.slice(0, half) + "\n...[STDERR TRUNCATED]";
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
this._allExecutions.push({
|
|
2093
|
+
code: command,
|
|
2094
|
+
purpose: purpose || null,
|
|
2095
|
+
output: result.stdout,
|
|
2096
|
+
stderr: result.stderr,
|
|
2097
|
+
exitCode: result.exitCode,
|
|
2098
|
+
filePath: null,
|
|
2099
|
+
tool: "run_bash"
|
|
2100
|
+
});
|
|
2101
|
+
if (this.onCodeExecution) {
|
|
2102
|
+
try {
|
|
2103
|
+
this.onCodeExecution(command, result);
|
|
2104
|
+
} catch (e) {
|
|
2105
|
+
logger_default.warn(`onCodeExecution callback error: ${e.message}`);
|
|
2106
|
+
}
|
|
2107
|
+
}
|
|
2108
|
+
return result;
|
|
2109
|
+
}
|
|
1881
2110
|
/**
|
|
1882
|
-
* Format execution result as a string for the model.
|
|
1883
2111
|
* @private
|
|
1884
|
-
* @param {{stdout: string, stderr: string, exitCode: number}} result
|
|
1885
|
-
* @returns {string}
|
|
1886
2112
|
*/
|
|
1887
2113
|
_formatOutput(result) {
|
|
1888
2114
|
let output = "";
|
|
@@ -1891,20 +2117,121 @@ ${this._userSystemPrompt}`;
|
|
|
1891
2117
|
if (result.exitCode !== 0) output += (output ? "\n" : "") + `[EXIT CODE]: ${result.exitCode}`;
|
|
1892
2118
|
return output || "(no output)";
|
|
1893
2119
|
}
|
|
2120
|
+
// ── Tool Call Dispatch ───────────────────────────────────────────────────
|
|
2121
|
+
/**
|
|
2122
|
+
* Handle a tool call by name, dispatching to the appropriate handler.
|
|
2123
|
+
* @private
|
|
2124
|
+
* @param {string} name - Tool name
|
|
2125
|
+
* @param {Object} input - Tool arguments
|
|
2126
|
+
* @returns {Promise<{output: string, type: string, data: Object}>}
|
|
2127
|
+
*/
|
|
2128
|
+
async _handleToolCall(name, input) {
|
|
2129
|
+
switch (name) {
|
|
2130
|
+
case "execute_code":
|
|
2131
|
+
case "write_and_run_code": {
|
|
2132
|
+
const result = await this._executeCode(input.code || "", input.purpose, name);
|
|
2133
|
+
return {
|
|
2134
|
+
output: this._formatOutput(result),
|
|
2135
|
+
type: "code_execution",
|
|
2136
|
+
data: {
|
|
2137
|
+
tool: name,
|
|
2138
|
+
code: input.code || "",
|
|
2139
|
+
purpose: input.purpose,
|
|
2140
|
+
stdout: result.stdout,
|
|
2141
|
+
stderr: result.stderr,
|
|
2142
|
+
exitCode: result.exitCode,
|
|
2143
|
+
denied: result.denied
|
|
2144
|
+
}
|
|
2145
|
+
};
|
|
2146
|
+
}
|
|
2147
|
+
case "write_code": {
|
|
2148
|
+
return {
|
|
2149
|
+
output: "Code written successfully.",
|
|
2150
|
+
type: "write",
|
|
2151
|
+
data: {
|
|
2152
|
+
tool: "write_code",
|
|
2153
|
+
code: input.code || "",
|
|
2154
|
+
purpose: input.purpose,
|
|
2155
|
+
language: input.language || "javascript"
|
|
2156
|
+
}
|
|
2157
|
+
};
|
|
2158
|
+
}
|
|
2159
|
+
case "fix_code": {
|
|
2160
|
+
let execResult = null;
|
|
2161
|
+
if (input.execute) {
|
|
2162
|
+
execResult = await this._executeCode(input.fixed_code || "", "fix", "fix_code");
|
|
2163
|
+
}
|
|
2164
|
+
return {
|
|
2165
|
+
output: input.execute ? this._formatOutput(execResult) : "Fix recorded.",
|
|
2166
|
+
type: "fix",
|
|
2167
|
+
data: {
|
|
2168
|
+
tool: "fix_code",
|
|
2169
|
+
originalCode: input.original_code || "",
|
|
2170
|
+
fixedCode: input.fixed_code || "",
|
|
2171
|
+
explanation: input.explanation,
|
|
2172
|
+
executed: !!input.execute,
|
|
2173
|
+
stdout: execResult?.stdout,
|
|
2174
|
+
stderr: execResult?.stderr,
|
|
2175
|
+
exitCode: execResult?.exitCode,
|
|
2176
|
+
denied: execResult?.denied
|
|
2177
|
+
}
|
|
2178
|
+
};
|
|
2179
|
+
}
|
|
2180
|
+
case "run_bash": {
|
|
2181
|
+
const result = await this._executeBash(input.command || "", input.purpose);
|
|
2182
|
+
return {
|
|
2183
|
+
output: this._formatOutput(result),
|
|
2184
|
+
type: "bash",
|
|
2185
|
+
data: {
|
|
2186
|
+
tool: "run_bash",
|
|
2187
|
+
command: input.command || "",
|
|
2188
|
+
purpose: input.purpose,
|
|
2189
|
+
stdout: result.stdout,
|
|
2190
|
+
stderr: result.stderr,
|
|
2191
|
+
exitCode: result.exitCode,
|
|
2192
|
+
denied: result.denied
|
|
2193
|
+
}
|
|
2194
|
+
};
|
|
2195
|
+
}
|
|
2196
|
+
case "use_skill": {
|
|
2197
|
+
const skillName = input.skill_name || "";
|
|
2198
|
+
const skill = this._skillRegistry.get(skillName);
|
|
2199
|
+
if (!skill) {
|
|
2200
|
+
const available = [...this._skillRegistry.keys()].join(", ");
|
|
2201
|
+
return {
|
|
2202
|
+
output: `Skill "${skillName}" not found. Available skills: ${available || "(none)"}`,
|
|
2203
|
+
type: "skill",
|
|
2204
|
+
data: { tool: "use_skill", skillName, found: false }
|
|
2205
|
+
};
|
|
2206
|
+
}
|
|
2207
|
+
return {
|
|
2208
|
+
output: skill.content,
|
|
2209
|
+
type: "skill",
|
|
2210
|
+
data: { tool: "use_skill", skillName: skill.name, content: skill.content, found: true }
|
|
2211
|
+
};
|
|
2212
|
+
}
|
|
2213
|
+
default:
|
|
2214
|
+
return {
|
|
2215
|
+
output: `Unknown tool: ${name}`,
|
|
2216
|
+
type: "unknown",
|
|
2217
|
+
data: { tool: name }
|
|
2218
|
+
};
|
|
2219
|
+
}
|
|
2220
|
+
}
|
|
1894
2221
|
// ── Non-Streaming Chat ───────────────────────────────────────────────────
|
|
1895
2222
|
/**
|
|
1896
2223
|
* Send a message and get a complete response (non-streaming).
|
|
1897
|
-
* Automatically handles the
|
|
2224
|
+
* Automatically handles the multi-tool execution loop.
|
|
1898
2225
|
*
|
|
1899
2226
|
* @param {string} message - The user's message
|
|
1900
2227
|
* @param {Object} [opts={}] - Per-message options
|
|
1901
2228
|
* @param {Record<string, string>} [opts.labels] - Per-message billing labels
|
|
1902
|
-
* @returns {Promise<CodeAgentResponse>}
|
|
2229
|
+
* @returns {Promise<CodeAgentResponse>}
|
|
1903
2230
|
*/
|
|
1904
2231
|
async chat(message, opts = {}) {
|
|
1905
2232
|
if (!this.chatSession) await this.init();
|
|
1906
2233
|
this._stopped = false;
|
|
1907
|
-
const
|
|
2234
|
+
const toolCalls = [];
|
|
1908
2235
|
let consecutiveFailures = 0;
|
|
1909
2236
|
let response = await this._withRetry(() => this.chatSession.sendMessage({ message }));
|
|
1910
2237
|
for (let round = 0; round < this.maxRounds; round++) {
|
|
@@ -1914,31 +2241,26 @@ ${this._userSystemPrompt}`;
|
|
|
1914
2241
|
const results = [];
|
|
1915
2242
|
for (const call of functionCalls) {
|
|
1916
2243
|
if (this._stopped) break;
|
|
1917
|
-
const
|
|
1918
|
-
|
|
1919
|
-
const
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
});
|
|
1927
|
-
if (result.exitCode !== 0 && !result.denied) {
|
|
1928
|
-
consecutiveFailures++;
|
|
1929
|
-
} else {
|
|
1930
|
-
consecutiveFailures = 0;
|
|
2244
|
+
const { output, type, data } = await this._handleToolCall(call.name, call.args || {});
|
|
2245
|
+
toolCalls.push(data);
|
|
2246
|
+
const isExecutingTool = EXECUTING_TOOLS.has(call.name) || call.name === "fix_code" && call.args?.execute;
|
|
2247
|
+
if (isExecutingTool) {
|
|
2248
|
+
if (data.exitCode !== 0 && !data.denied) {
|
|
2249
|
+
consecutiveFailures++;
|
|
2250
|
+
} else {
|
|
2251
|
+
consecutiveFailures = 0;
|
|
2252
|
+
}
|
|
1931
2253
|
}
|
|
1932
|
-
let
|
|
2254
|
+
let toolOutput = output;
|
|
1933
2255
|
if (consecutiveFailures >= this.maxRetries) {
|
|
1934
|
-
|
|
2256
|
+
toolOutput += `
|
|
1935
2257
|
|
|
1936
2258
|
[RETRY LIMIT REACHED] You have failed ${this.maxRetries} consecutive attempts. STOP trying to execute code. Instead, respond with: 1) What you were trying to do, 2) The errors you encountered, 3) Questions for the user about how to resolve it.`;
|
|
1937
2259
|
}
|
|
1938
2260
|
results.push({
|
|
1939
2261
|
id: call.id,
|
|
1940
2262
|
name: call.name,
|
|
1941
|
-
result:
|
|
2263
|
+
result: toolOutput
|
|
1942
2264
|
});
|
|
1943
2265
|
}
|
|
1944
2266
|
if (this._stopped) break;
|
|
@@ -1960,31 +2282,42 @@ ${this._userSystemPrompt}`;
|
|
|
1960
2282
|
totalTokens: this.lastResponseMetadata.totalTokens,
|
|
1961
2283
|
attempts: 1
|
|
1962
2284
|
};
|
|
2285
|
+
const codeExecutions = toolCalls.filter((tc) => tc.tool === "execute_code" || tc.tool === "write_and_run_code" || tc.tool === "fix_code" && tc.executed).map((tc) => ({
|
|
2286
|
+
code: tc.code || tc.fixedCode,
|
|
2287
|
+
purpose: this._slugify(tc.purpose),
|
|
2288
|
+
output: tc.stdout || "",
|
|
2289
|
+
stderr: tc.stderr || "",
|
|
2290
|
+
exitCode: tc.exitCode ?? 0
|
|
2291
|
+
}));
|
|
1963
2292
|
return {
|
|
1964
2293
|
text: response.text || "",
|
|
1965
2294
|
codeExecutions,
|
|
2295
|
+
toolCalls,
|
|
1966
2296
|
usage: this.getLastUsage()
|
|
1967
2297
|
};
|
|
1968
2298
|
}
|
|
1969
2299
|
// ── Streaming ────────────────────────────────────────────────────────────
|
|
1970
2300
|
/**
|
|
1971
2301
|
* Send a message and stream the response as events.
|
|
1972
|
-
* Automatically handles the code execution loop between streamed rounds.
|
|
1973
2302
|
*
|
|
1974
2303
|
* Event types:
|
|
1975
2304
|
* - `text` — A chunk of the agent's text response
|
|
1976
|
-
* - `code` — The agent is about to execute code
|
|
1977
|
-
* - `output` — Code finished executing
|
|
2305
|
+
* - `code` — The agent is about to execute code (execute_code or write_and_run_code)
|
|
2306
|
+
* - `output` — Code/bash finished executing
|
|
2307
|
+
* - `write` — The agent wrote code without executing (write_code)
|
|
2308
|
+
* - `fix` — The agent fixed code (fix_code)
|
|
2309
|
+
* - `bash` — The agent is about to run a bash command
|
|
2310
|
+
* - `skill` — The agent loaded a skill
|
|
1978
2311
|
* - `done` — The agent finished
|
|
1979
2312
|
*
|
|
1980
2313
|
* @param {string} message - The user's message
|
|
1981
|
-
* @param {Object} [opts={}]
|
|
2314
|
+
* @param {Object} [opts={}]
|
|
1982
2315
|
* @yields {CodeAgentStreamEvent}
|
|
1983
2316
|
*/
|
|
1984
2317
|
async *stream(message, opts = {}) {
|
|
1985
2318
|
if (!this.chatSession) await this.init();
|
|
1986
2319
|
this._stopped = false;
|
|
1987
|
-
const
|
|
2320
|
+
const toolCalls = [];
|
|
1988
2321
|
let fullText = "";
|
|
1989
2322
|
let consecutiveFailures = 0;
|
|
1990
2323
|
let streamResponse = await this._withRetry(() => this.chatSession.sendMessageStream({ message }));
|
|
@@ -2001,50 +2334,62 @@ ${this._userSystemPrompt}`;
|
|
|
2001
2334
|
}
|
|
2002
2335
|
}
|
|
2003
2336
|
if (functionCalls.length === 0) {
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2337
|
+
const codeExecutions2 = toolCalls.filter((tc) => tc.tool === "execute_code" || tc.tool === "write_and_run_code" || tc.tool === "fix_code" && tc.executed).map((tc) => ({
|
|
2338
|
+
code: tc.code || tc.fixedCode,
|
|
2339
|
+
purpose: this._slugify(tc.purpose),
|
|
2340
|
+
output: tc.stdout || "",
|
|
2341
|
+
stderr: tc.stderr || "",
|
|
2342
|
+
exitCode: tc.exitCode ?? 0
|
|
2343
|
+
}));
|
|
2344
|
+
yield { type: "done", fullText, codeExecutions: codeExecutions2, toolCalls, usage: this.getLastUsage() };
|
|
2010
2345
|
return;
|
|
2011
2346
|
}
|
|
2012
2347
|
const results = [];
|
|
2013
2348
|
for (const call of functionCalls) {
|
|
2014
2349
|
if (this._stopped) break;
|
|
2015
|
-
const
|
|
2016
|
-
const
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
}
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2350
|
+
const toolName = call.name;
|
|
2351
|
+
const toolInput = call.args || {};
|
|
2352
|
+
if (toolName === "write_code") {
|
|
2353
|
+
yield { type: "write", code: toolInput.code, purpose: toolInput.purpose, language: toolInput.language || "javascript" };
|
|
2354
|
+
} else if (toolName === "fix_code") {
|
|
2355
|
+
yield { type: "fix", originalCode: toolInput.original_code, fixedCode: toolInput.fixed_code, explanation: toolInput.explanation };
|
|
2356
|
+
} else if (toolName === "run_bash") {
|
|
2357
|
+
yield { type: "bash", command: toolInput.command };
|
|
2358
|
+
} else if (toolName === "execute_code" || toolName === "write_and_run_code") {
|
|
2359
|
+
yield { type: "code", code: toolInput.code };
|
|
2360
|
+
}
|
|
2361
|
+
const { output, type, data } = await this._handleToolCall(toolName, toolInput);
|
|
2362
|
+
toolCalls.push(data);
|
|
2363
|
+
if (data.stdout !== void 0 || data.stderr !== void 0) {
|
|
2364
|
+
yield {
|
|
2365
|
+
type: "output",
|
|
2366
|
+
code: data.code || data.command || data.fixedCode,
|
|
2367
|
+
stdout: data.stdout || "",
|
|
2368
|
+
stderr: data.stderr || "",
|
|
2369
|
+
exitCode: data.exitCode ?? 0
|
|
2370
|
+
};
|
|
2371
|
+
}
|
|
2372
|
+
if (toolName === "use_skill") {
|
|
2373
|
+
yield { type: "skill", skillName: data.skillName, content: data.content, found: data.found };
|
|
2374
|
+
}
|
|
2375
|
+
const isExecutingTool = EXECUTING_TOOLS.has(toolName) || toolName === "fix_code" && toolInput.execute;
|
|
2376
|
+
if (isExecutingTool) {
|
|
2377
|
+
if (data.exitCode !== 0 && !data.denied) {
|
|
2378
|
+
consecutiveFailures++;
|
|
2379
|
+
} else {
|
|
2380
|
+
consecutiveFailures = 0;
|
|
2381
|
+
}
|
|
2037
2382
|
}
|
|
2038
|
-
let
|
|
2383
|
+
let toolOutput = output;
|
|
2039
2384
|
if (consecutiveFailures >= this.maxRetries) {
|
|
2040
|
-
|
|
2385
|
+
toolOutput += `
|
|
2041
2386
|
|
|
2042
2387
|
[RETRY LIMIT REACHED] You have failed ${this.maxRetries} consecutive attempts. STOP trying to execute code. Instead, respond with: 1) What you were trying to do, 2) The errors you encountered, 3) Questions for the user about how to resolve it.`;
|
|
2043
2388
|
}
|
|
2044
2389
|
results.push({
|
|
2045
2390
|
id: call.id,
|
|
2046
2391
|
name: call.name,
|
|
2047
|
-
result:
|
|
2392
|
+
result: toolOutput
|
|
2048
2393
|
});
|
|
2049
2394
|
}
|
|
2050
2395
|
if (this._stopped) break;
|
|
@@ -2062,31 +2407,32 @@ ${this._userSystemPrompt}`;
|
|
|
2062
2407
|
let warning = "Max tool rounds reached";
|
|
2063
2408
|
if (this._stopped) warning = "Agent was stopped";
|
|
2064
2409
|
else if (consecutiveFailures >= this.maxRetries) warning = "Retry limit reached";
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
};
|
|
2410
|
+
const codeExecutions = toolCalls.filter((tc) => tc.tool === "execute_code" || tc.tool === "write_and_run_code" || tc.tool === "fix_code" && tc.executed).map((tc) => ({
|
|
2411
|
+
code: tc.code || tc.fixedCode,
|
|
2412
|
+
purpose: this._slugify(tc.purpose),
|
|
2413
|
+
output: tc.stdout || "",
|
|
2414
|
+
stderr: tc.stderr || "",
|
|
2415
|
+
exitCode: tc.exitCode ?? 0
|
|
2416
|
+
}));
|
|
2417
|
+
yield { type: "done", fullText, codeExecutions, toolCalls, usage: this.getLastUsage(), warning };
|
|
2072
2418
|
}
|
|
2073
2419
|
// ── Dump ─────────────────────────────────────────────────────────────────
|
|
2074
2420
|
/**
|
|
2075
|
-
* Returns all code scripts the agent has
|
|
2076
|
-
* @returns {Array<{fileName: string, script: string}>}
|
|
2421
|
+
* Returns all code scripts and bash commands the agent has executed.
|
|
2422
|
+
* @returns {Array<{fileName: string, purpose: string|null, script: string, filePath: string|null, tool: string}>}
|
|
2077
2423
|
*/
|
|
2078
2424
|
dump() {
|
|
2079
2425
|
return this._allExecutions.map((exec, i) => ({
|
|
2080
2426
|
fileName: exec.purpose ? `agent-${exec.purpose}.mjs` : `script-${i + 1}.mjs`,
|
|
2081
2427
|
purpose: exec.purpose || null,
|
|
2082
2428
|
script: exec.code,
|
|
2083
|
-
filePath: exec.filePath || null
|
|
2429
|
+
filePath: exec.filePath || null,
|
|
2430
|
+
tool: exec.tool || "execute_code"
|
|
2084
2431
|
}));
|
|
2085
2432
|
}
|
|
2086
2433
|
// ── Stop ─────────────────────────────────────────────────────────────────
|
|
2087
2434
|
/**
|
|
2088
|
-
* Stop the agent
|
|
2089
|
-
* If a child process is currently running, it will be killed.
|
|
2435
|
+
* Stop the agent. Kills any running child process.
|
|
2090
2436
|
*/
|
|
2091
2437
|
stop() {
|
|
2092
2438
|
this._stopped = true;
|