open-agents-ai 0.96.0 → 0.97.0
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/index.js
CHANGED
|
@@ -1467,6 +1467,30 @@ ${stderr}` : ""),
|
|
|
1467
1467
|
// packages/execution/dist/tools/file-read.js
|
|
1468
1468
|
import { readFile } from "node:fs/promises";
|
|
1469
1469
|
import { resolve } from "node:path";
|
|
1470
|
+
function extractPath(args) {
|
|
1471
|
+
if (typeof args["path"] === "string" && args["path"].trim()) {
|
|
1472
|
+
return args["path"].trim();
|
|
1473
|
+
}
|
|
1474
|
+
for (const key of ["file", "file_path", "filename", "filepath", "filePath"]) {
|
|
1475
|
+
if (typeof args[key] === "string" && args[key].trim()) {
|
|
1476
|
+
return args[key].trim();
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
if (typeof args["_raw"] === "string") {
|
|
1480
|
+
const raw = args["_raw"].trim();
|
|
1481
|
+
const match = raw.match(/["']?(?:path|file|filename)["']?\s*[:=]\s*["']([^"'\s}]+)["']/i);
|
|
1482
|
+
if (match?.[1])
|
|
1483
|
+
return match[1];
|
|
1484
|
+
if (raw && !raw.includes("{") && !raw.includes(":") && raw.length < 500) {
|
|
1485
|
+
return raw;
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
const stringVals = Object.values(args).filter((v) => typeof v === "string" && v.trim().length > 0 && v.length < 500);
|
|
1489
|
+
if (stringVals.length === 1 && /[./]/.test(stringVals[0])) {
|
|
1490
|
+
return stringVals[0].trim();
|
|
1491
|
+
}
|
|
1492
|
+
return null;
|
|
1493
|
+
}
|
|
1470
1494
|
var FileReadTool;
|
|
1471
1495
|
var init_file_read = __esm({
|
|
1472
1496
|
"packages/execution/dist/tools/file-read.js"() {
|
|
@@ -1493,15 +1517,15 @@ var init_file_read = __esm({
|
|
|
1493
1517
|
this._contextWindowSize = size;
|
|
1494
1518
|
}
|
|
1495
1519
|
async execute(args) {
|
|
1496
|
-
const filePath = args
|
|
1520
|
+
const filePath = extractPath(args);
|
|
1497
1521
|
const offset = args["offset"];
|
|
1498
1522
|
const limit = args["limit"];
|
|
1499
1523
|
const start = performance.now();
|
|
1500
|
-
if (!filePath
|
|
1524
|
+
if (!filePath) {
|
|
1501
1525
|
return {
|
|
1502
1526
|
success: false,
|
|
1503
1527
|
output: "",
|
|
1504
|
-
error: "
|
|
1528
|
+
error: `file_read requires "path" parameter. Call: file_read({"path": "src/example.ts"})`,
|
|
1505
1529
|
durationMs: performance.now() - start
|
|
1506
1530
|
};
|
|
1507
1531
|
}
|
|
@@ -1534,10 +1558,21 @@ var init_file_read = __esm({
|
|
|
1534
1558
|
durationMs: performance.now() - start
|
|
1535
1559
|
};
|
|
1536
1560
|
} catch (error) {
|
|
1561
|
+
const errMsg = error instanceof Error ? error.message : String(error);
|
|
1562
|
+
if (/ENOENT|no such file/i.test(errMsg)) {
|
|
1563
|
+
const hasSlash = filePath.includes("/");
|
|
1564
|
+
const hint = hasSlash ? `File not found: "${filePath}". Use list_directory on the parent directory to discover valid paths.` : `File not found: "${filePath}". This name is not a valid path. If you saw "${filePath}" inside a directory listing, use the FULL path (e.g., "parent_dir/${filePath}"). If it is a directory, use list_directory("${filePath}") instead of file_read.`;
|
|
1565
|
+
return {
|
|
1566
|
+
success: false,
|
|
1567
|
+
output: "",
|
|
1568
|
+
error: hint,
|
|
1569
|
+
durationMs: performance.now() - start
|
|
1570
|
+
};
|
|
1571
|
+
}
|
|
1537
1572
|
return {
|
|
1538
1573
|
success: false,
|
|
1539
1574
|
output: "",
|
|
1540
|
-
error:
|
|
1575
|
+
error: errMsg,
|
|
1541
1576
|
durationMs: performance.now() - start
|
|
1542
1577
|
};
|
|
1543
1578
|
}
|
|
@@ -1549,6 +1584,14 @@ var init_file_read = __esm({
|
|
|
1549
1584
|
// packages/execution/dist/tools/file-write.js
|
|
1550
1585
|
import { writeFile, mkdir } from "node:fs/promises";
|
|
1551
1586
|
import { resolve as resolve2, dirname } from "node:path";
|
|
1587
|
+
function extractWritePath(args) {
|
|
1588
|
+
for (const key of ["path", "file", "file_path", "filename", "filepath", "filePath"]) {
|
|
1589
|
+
if (typeof args[key] === "string" && args[key].trim()) {
|
|
1590
|
+
return args[key].trim();
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
return null;
|
|
1594
|
+
}
|
|
1552
1595
|
var FileWriteTool;
|
|
1553
1596
|
var init_file_write = __esm({
|
|
1554
1597
|
"packages/execution/dist/tools/file-write.js"() {
|
|
@@ -1569,14 +1612,14 @@ var init_file_write = __esm({
|
|
|
1569
1612
|
this.workingDir = workingDir;
|
|
1570
1613
|
}
|
|
1571
1614
|
async execute(args) {
|
|
1572
|
-
const filePath = args
|
|
1573
|
-
const content = args["content"];
|
|
1615
|
+
const filePath = extractWritePath(args);
|
|
1616
|
+
const content = args["content"] ?? args["text"] ?? args["data"];
|
|
1574
1617
|
const start = performance.now();
|
|
1575
|
-
if (!filePath
|
|
1618
|
+
if (!filePath) {
|
|
1576
1619
|
return {
|
|
1577
1620
|
success: false,
|
|
1578
1621
|
output: "",
|
|
1579
|
-
error: "
|
|
1622
|
+
error: `file_write requires "path" parameter. Call: file_write({"path": "src/file.ts", "content": "..."})`,
|
|
1580
1623
|
durationMs: performance.now() - start
|
|
1581
1624
|
};
|
|
1582
1625
|
}
|
|
@@ -1584,7 +1627,7 @@ var init_file_write = __esm({
|
|
|
1584
1627
|
return {
|
|
1585
1628
|
success: false,
|
|
1586
1629
|
output: "",
|
|
1587
|
-
error: "
|
|
1630
|
+
error: `file_write requires "content" parameter. Call: file_write({"path": "${filePath}", "content": "..."})`,
|
|
1588
1631
|
durationMs: performance.now() - start
|
|
1589
1632
|
};
|
|
1590
1633
|
}
|
|
@@ -2425,6 +2468,14 @@ Meta: ${metaKeys.map((k) => `${k}="${meta[k]?.slice(0, 80)}"`).join(", ")}`);
|
|
|
2425
2468
|
// packages/execution/dist/tools/file-edit.js
|
|
2426
2469
|
import { readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
|
|
2427
2470
|
import { resolve as resolve5 } from "node:path";
|
|
2471
|
+
function extractEditPath(args) {
|
|
2472
|
+
for (const key of ["path", "file", "file_path", "filename", "filepath", "filePath"]) {
|
|
2473
|
+
if (typeof args[key] === "string" && args[key].trim()) {
|
|
2474
|
+
return args[key].trim();
|
|
2475
|
+
}
|
|
2476
|
+
}
|
|
2477
|
+
return null;
|
|
2478
|
+
}
|
|
2428
2479
|
function countOccurrences(haystack, needle) {
|
|
2429
2480
|
let count = 0;
|
|
2430
2481
|
let pos = 0;
|
|
@@ -2478,16 +2529,16 @@ var init_file_edit = __esm({
|
|
|
2478
2529
|
this.workingDir = workingDir;
|
|
2479
2530
|
}
|
|
2480
2531
|
async execute(args) {
|
|
2481
|
-
const filePath = args
|
|
2482
|
-
const oldString = args["old_string"];
|
|
2483
|
-
const newString = args["new_string"];
|
|
2484
|
-
const replaceAll = args["replace_all"] === true;
|
|
2532
|
+
const filePath = extractEditPath(args);
|
|
2533
|
+
const oldString = args["old_string"] ?? args["oldString"] ?? args["search"] ?? args["find"];
|
|
2534
|
+
const newString = args["new_string"] ?? args["newString"] ?? args["replace"] ?? args["replacement"];
|
|
2535
|
+
const replaceAll = args["replace_all"] === true || args["replaceAll"] === true;
|
|
2485
2536
|
const start = performance.now();
|
|
2486
|
-
if (!filePath
|
|
2537
|
+
if (!filePath) {
|
|
2487
2538
|
return {
|
|
2488
2539
|
success: false,
|
|
2489
2540
|
output: "",
|
|
2490
|
-
error: "
|
|
2541
|
+
error: `file_edit requires "path" parameter. Call: file_edit({"path": "src/file.ts", "old_string": "...", "new_string": "..."})`,
|
|
2491
2542
|
durationMs: performance.now() - start
|
|
2492
2543
|
};
|
|
2493
2544
|
}
|
|
@@ -2495,7 +2546,7 @@ var init_file_edit = __esm({
|
|
|
2495
2546
|
return {
|
|
2496
2547
|
success: false,
|
|
2497
2548
|
output: "",
|
|
2498
|
-
error: "
|
|
2549
|
+
error: `file_edit requires "old_string" parameter. Call: file_edit({"path": "${filePath}", "old_string": "text to find", "new_string": "replacement"})`,
|
|
2499
2550
|
durationMs: performance.now() - start
|
|
2500
2551
|
};
|
|
2501
2552
|
}
|
|
@@ -2503,7 +2554,7 @@ var init_file_edit = __esm({
|
|
|
2503
2554
|
return {
|
|
2504
2555
|
success: false,
|
|
2505
2556
|
output: "",
|
|
2506
|
-
error: "
|
|
2557
|
+
error: `file_edit requires "new_string" parameter. Call: file_edit({"path": "${filePath}", "old_string": "...", "new_string": "replacement text"})`,
|
|
2507
2558
|
durationMs: performance.now() - start
|
|
2508
2559
|
};
|
|
2509
2560
|
}
|
|
@@ -3153,7 +3204,7 @@ var init_list_directory = __esm({
|
|
|
3153
3204
|
MAX_ENTRIES = 100;
|
|
3154
3205
|
ListDirectoryTool = class {
|
|
3155
3206
|
name = "list_directory";
|
|
3156
|
-
description = "List files and directories at a given path. Shows file sizes and types.";
|
|
3207
|
+
description = "List files and directories at a given path. Shows file sizes and types. Output includes full relative paths you can use directly in subsequent tool calls.";
|
|
3157
3208
|
parameters = {
|
|
3158
3209
|
type: "object",
|
|
3159
3210
|
properties: {
|
|
@@ -3177,18 +3228,40 @@ var init_list_directory = __esm({
|
|
|
3177
3228
|
const entries = readdirSync(fullPath, { withFileTypes: true });
|
|
3178
3229
|
const visible = entries.filter((e) => !EXCLUDED.has(e.name));
|
|
3179
3230
|
const limited = visible.slice(0, MAX_ENTRIES);
|
|
3231
|
+
const dirs = [];
|
|
3232
|
+
const files = [];
|
|
3180
3233
|
const lines = limited.map((entry) => {
|
|
3181
|
-
const
|
|
3234
|
+
const relPath = dirPath === "." ? entry.name : join7(dirPath, entry.name);
|
|
3182
3235
|
if (entry.isDirectory()) {
|
|
3183
|
-
|
|
3236
|
+
dirs.push(relPath);
|
|
3237
|
+
return `d ${relPath}/`;
|
|
3184
3238
|
}
|
|
3185
3239
|
let size = 0;
|
|
3186
3240
|
try {
|
|
3187
3241
|
size = statSync(join7(fullPath, entry.name)).size;
|
|
3188
3242
|
} catch {
|
|
3189
3243
|
}
|
|
3190
|
-
|
|
3244
|
+
files.push(relPath);
|
|
3245
|
+
return `f ${relPath} ${size}`;
|
|
3191
3246
|
});
|
|
3247
|
+
if (dirs.length > 0 || files.length > 0) {
|
|
3248
|
+
lines.push("");
|
|
3249
|
+
lines.push("Next steps \u2014 use these exact paths:");
|
|
3250
|
+
if (dirs.length > 0) {
|
|
3251
|
+
for (const d of dirs.slice(0, 8)) {
|
|
3252
|
+
lines.push(` list_directory("${d}")`);
|
|
3253
|
+
}
|
|
3254
|
+
if (dirs.length > 8)
|
|
3255
|
+
lines.push(` ... and ${dirs.length - 8} more directories`);
|
|
3256
|
+
}
|
|
3257
|
+
if (files.length > 0) {
|
|
3258
|
+
for (const f of files.slice(0, 5)) {
|
|
3259
|
+
lines.push(` file_read("${f}")`);
|
|
3260
|
+
}
|
|
3261
|
+
if (files.length > 5)
|
|
3262
|
+
lines.push(` ... and ${files.length - 5} more files`);
|
|
3263
|
+
}
|
|
3264
|
+
}
|
|
3192
3265
|
return {
|
|
3193
3266
|
success: true,
|
|
3194
3267
|
output: lines.join("\n"),
|
|
@@ -17104,14 +17177,15 @@ ${result.output.length > maxLen ? this.foldOutput(result.output, maxLen) : resul
|
|
|
17104
17177
|
});
|
|
17105
17178
|
}
|
|
17106
17179
|
const enoentTools = /* @__PURE__ */ new Set(["file_read", "list_directory", "find_files"]);
|
|
17107
|
-
if (!result.success && enoentTools.has(tc.name) && /ENOENT|no such file|does not exist/i.test(result.error ?? result.output)) {
|
|
17180
|
+
if (!result.success && enoentTools.has(tc.name) && /ENOENT|no such file|does not exist|not found/i.test(result.error ?? result.output)) {
|
|
17108
17181
|
this._consecutiveEnoent++;
|
|
17109
17182
|
if (this._consecutiveEnoent >= 2) {
|
|
17110
|
-
|
|
17111
|
-
|
|
17112
|
-
|
|
17113
|
-
|
|
17114
|
-
|
|
17183
|
+
const failedPath = String(tc.arguments?.["path"] ?? tc.arguments?.["file"] ?? "unknown");
|
|
17184
|
+
this.pendingUserMessages.push(`[SYSTEM] ${this._consecutiveEnoent} consecutive file-not-found errors (last: "${failedPath}"). You are repeating failed paths. CHANGE YOUR APPROACH:
|
|
17185
|
+
1. Call list_directory(".") to see what exists at the project root
|
|
17186
|
+
2. Entries in a directory listing are RELATIVE to that directory. If you listed "parent/" and saw "child", the full path is "parent/child" \u2014 NOT ".child" or just "child"
|
|
17187
|
+
3. If an entry is a directory (marked "d"), use list_directory on it, NOT file_read
|
|
17188
|
+
4. Use the FULL relative paths shown in list_directory output \u2014 they are copy-paste ready`);
|
|
17115
17189
|
}
|
|
17116
17190
|
} else {
|
|
17117
17191
|
this._consecutiveEnoent = 0;
|
|
@@ -17359,14 +17433,15 @@ Integrate this guidance into your current approach. Continue working on the task
|
|
|
17359
17433
|
${result.output}`;
|
|
17360
17434
|
this.emit({ type: "tool_result", toolName: tc.name, content: output.slice(0, 200), success: result.success, turn, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
17361
17435
|
const enoentTools2 = /* @__PURE__ */ new Set(["file_read", "list_directory", "find_files"]);
|
|
17362
|
-
if (!result.success && enoentTools2.has(tc.name) && /ENOENT|no such file|does not exist/i.test(result.error ?? result.output)) {
|
|
17436
|
+
if (!result.success && enoentTools2.has(tc.name) && /ENOENT|no such file|does not exist|not found/i.test(result.error ?? result.output)) {
|
|
17363
17437
|
this._consecutiveEnoent++;
|
|
17364
17438
|
if (this._consecutiveEnoent >= 2) {
|
|
17365
|
-
|
|
17366
|
-
|
|
17367
|
-
|
|
17368
|
-
|
|
17369
|
-
|
|
17439
|
+
const failedPath2 = String(tc.arguments?.["path"] ?? tc.arguments?.["file"] ?? "unknown");
|
|
17440
|
+
this.pendingUserMessages.push(`[SYSTEM] ${this._consecutiveEnoent} consecutive file-not-found errors (last: "${failedPath2}"). You are repeating failed paths. CHANGE YOUR APPROACH:
|
|
17441
|
+
1. Call list_directory(".") to see what exists at the project root
|
|
17442
|
+
2. Entries in a directory listing are RELATIVE to that directory. If you listed "parent/" and saw "child", the full path is "parent/child" \u2014 NOT ".child" or just "child"
|
|
17443
|
+
3. If an entry is a directory (marked "d"), use list_directory on it, NOT file_read
|
|
17444
|
+
4. Use the FULL relative paths shown in list_directory output \u2014 they are copy-paste ready`);
|
|
17370
17445
|
}
|
|
17371
17446
|
} else {
|
|
17372
17447
|
this._consecutiveEnoent = 0;
|
package/package.json
CHANGED
|
@@ -385,3 +385,11 @@ When a file_read, list_directory, or find_files call returns ENOENT (file/direct
|
|
|
385
385
|
- If the missing path came from memory, update memory to remove the stale reference
|
|
386
386
|
- After discovering the real structure, navigate to the correct path
|
|
387
387
|
- Never make more than 2 consecutive attempts at paths that don't exist
|
|
388
|
+
|
|
389
|
+
## Directory Listing Path Rules
|
|
390
|
+
|
|
391
|
+
Entries in a directory listing are RELATIVE to the directory you listed.
|
|
392
|
+
- If you call list_directory(".oa") and see "context", the full path is ".oa/context" — NOT ".context" or "context"
|
|
393
|
+
- If an entry is marked "d" (directory), use list_directory on it — NOT file_read
|
|
394
|
+
- list_directory output includes full relative paths you can copy directly into your next tool call
|
|
395
|
+
- Prefer list_directory over shell ls — it shows full relative paths you can copy directly into your next tool call
|
|
@@ -42,3 +42,6 @@ NEVER say "I can't do that". ALWAYS attempt the task using your tools. If a tool
|
|
|
42
42
|
- You MUST call task_complete when done
|
|
43
43
|
- Do NOT output long explanations. Focus on tool calls.
|
|
44
44
|
- If file_read/list_directory returns ENOENT, use list_directory on the project root — do NOT guess parent paths
|
|
45
|
+
- Directory listing entries are RELATIVE to the listed directory. If you list "parent/" and see "child", the full path is "parent/child" — NOT ".child" or just "child"
|
|
46
|
+
- If an entry is a directory (d), use list_directory on it — NOT file_read
|
|
47
|
+
- Prefer list_directory over shell ls — it shows full paths ready for your next tool call
|
|
@@ -15,3 +15,5 @@ Rules:
|
|
|
15
15
|
- Run tests after every change.
|
|
16
16
|
- Call task_complete when done.
|
|
17
17
|
- If ENOENT, list_directory on project root. Don't guess paths.
|
|
18
|
+
- Directory entries are RELATIVE. If you list "parent/" and see "child", the path is "parent/child" — NOT ".child".
|
|
19
|
+
- Use list_directory for directories, NOT file_read. Prefer list_directory over shell ls.
|