open-agents-ai 0.96.1 → 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
@@ -1558,10 +1558,21 @@ var init_file_read = __esm({
1558
1558
  durationMs: performance.now() - start
1559
1559
  };
1560
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
+ }
1561
1572
  return {
1562
1573
  success: false,
1563
1574
  output: "",
1564
- error: error instanceof Error ? error.message : String(error),
1575
+ error: errMsg,
1565
1576
  durationMs: performance.now() - start
1566
1577
  };
1567
1578
  }
@@ -3193,7 +3204,7 @@ var init_list_directory = __esm({
3193
3204
  MAX_ENTRIES = 100;
3194
3205
  ListDirectoryTool = class {
3195
3206
  name = "list_directory";
3196
- 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.";
3197
3208
  parameters = {
3198
3209
  type: "object",
3199
3210
  properties: {
@@ -3217,18 +3228,40 @@ var init_list_directory = __esm({
3217
3228
  const entries = readdirSync(fullPath, { withFileTypes: true });
3218
3229
  const visible = entries.filter((e) => !EXCLUDED.has(e.name));
3219
3230
  const limited = visible.slice(0, MAX_ENTRIES);
3231
+ const dirs = [];
3232
+ const files = [];
3220
3233
  const lines = limited.map((entry) => {
3221
- const prefix = entry.isDirectory() ? "d" : "f";
3234
+ const relPath = dirPath === "." ? entry.name : join7(dirPath, entry.name);
3222
3235
  if (entry.isDirectory()) {
3223
- return `${prefix} ${entry.name}`;
3236
+ dirs.push(relPath);
3237
+ return `d ${relPath}/`;
3224
3238
  }
3225
3239
  let size = 0;
3226
3240
  try {
3227
3241
  size = statSync(join7(fullPath, entry.name)).size;
3228
3242
  } catch {
3229
3243
  }
3230
- return `${prefix} ${entry.name} ${size}`;
3244
+ files.push(relPath);
3245
+ return `f ${relPath} ${size}`;
3231
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
+ }
3232
3265
  return {
3233
3266
  success: true,
3234
3267
  output: lines.join("\n"),
@@ -17144,14 +17177,15 @@ ${result.output.length > maxLen ? this.foldOutput(result.output, maxLen) : resul
17144
17177
  });
17145
17178
  }
17146
17179
  const enoentTools = /* @__PURE__ */ new Set(["file_read", "list_directory", "find_files"]);
17147
- 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)) {
17148
17181
  this._consecutiveEnoent++;
17149
17182
  if (this._consecutiveEnoent >= 2) {
17150
- this.pendingUserMessages.push(`[SYSTEM] You have hit ${this._consecutiveEnoent} consecutive ENOENT errors. STOP guessing paths. Instead:
17151
- 1. Use list_directory on the PROJECT ROOT to discover what actually exists
17152
- 2. If the missing path came from memory, use memory_write to remove the stale reference
17153
- 3. Navigate from the root to find the correct location
17154
- Do NOT continue walking up parent directories.`);
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`);
17155
17189
  }
17156
17190
  } else {
17157
17191
  this._consecutiveEnoent = 0;
@@ -17399,14 +17433,15 @@ Integrate this guidance into your current approach. Continue working on the task
17399
17433
  ${result.output}`;
17400
17434
  this.emit({ type: "tool_result", toolName: tc.name, content: output.slice(0, 200), success: result.success, turn, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
17401
17435
  const enoentTools2 = /* @__PURE__ */ new Set(["file_read", "list_directory", "find_files"]);
17402
- 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)) {
17403
17437
  this._consecutiveEnoent++;
17404
17438
  if (this._consecutiveEnoent >= 2) {
17405
- this.pendingUserMessages.push(`[SYSTEM] You have hit ${this._consecutiveEnoent} consecutive ENOENT errors. STOP guessing paths. Instead:
17406
- 1. Use list_directory on the PROJECT ROOT to discover what actually exists
17407
- 2. If the missing path came from memory, use memory_write to remove the stale reference
17408
- 3. Navigate from the root to find the correct location
17409
- Do NOT continue walking up parent directories.`);
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`);
17410
17445
  }
17411
17446
  } else {
17412
17447
  this._consecutiveEnoent = 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.96.1",
3
+ "version": "0.97.0",
4
4
  "description": "AI coding agent powered by open-source models (Ollama/vLLM) — interactive TUI with agentic tool-calling loop",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -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.