Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions packages/builtin-tools/src/tools/FileEditTool/FileEditTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,14 +178,26 @@ export const FileEditTool = buildTool({

const fs = getFsImplementation()

// Prevent OOM on multi-GB files.
// 预检:防止 OOM(超大文件)+ 防止编辑目录路径
// 目录路径预检避免 AI 模型传入目录时触发原始 EISDIR 错误,
// 确保模型拿到清晰、可操作的指引消息。
try {
const { size } = await fs.stat(fullFilePath)
if (size > MAX_EDIT_FILE_SIZE) {
const fileStat = await fs.stat(fullFilePath)
// 预检:如果路径已是一个存在的目录,及时拒绝并给出指引
// 避免 AI 模型传入目录路径后 Edit 工具读取文件时抛 EISDIR。
if (fileStat.isDirectory()) {
return {
result: false,
behavior: 'ask',
message: `File is too large to edit (${formatFileSize(size)}). Maximum editable file size is ${formatFileSize(MAX_EDIT_FILE_SIZE)}.`,
message: `Cannot edit '${file_path}': the specified path is an existing directory. Use Bash ls to list files in this directory, or specify a filename path to edit a specific file.`,
errorCode: 10,
}
}
if (fileStat.size > MAX_EDIT_FILE_SIZE) {
return {
result: false,
behavior: 'ask',
message: `File is too large to edit (${formatFileSize(fileStat.size)}). Maximum editable file size is ${formatFileSize(MAX_EDIT_FILE_SIZE)}.`,
errorCode: 10,
}
Comment on lines +188 to 202

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Duplicate error code 10.

The new directory path check uses errorCode: 10, which is identical to the existing error code used for the file size check below it. If the client or AI model relies on these error codes to uniquely identify the failure reason and determine recovery steps, sharing the same code could cause incorrect behavior. Consider assigning a unique error code to one of them.

💡 Proposed fix to make error codes unique
       if (fileStat.size > MAX_EDIT_FILE_SIZE) {
         return {
           result: false,
           behavior: 'ask',
           message: `File is too large to edit (${formatFileSize(fileStat.size)}). Maximum editable file size is ${formatFileSize(MAX_EDIT_FILE_SIZE)}.`,
-          errorCode: 10,
+          errorCode: 11,
         }
       }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (fileStat.isDirectory()) {
return {
result: false,
behavior: 'ask',
message: `File is too large to edit (${formatFileSize(size)}). Maximum editable file size is ${formatFileSize(MAX_EDIT_FILE_SIZE)}.`,
message: `Cannot edit '${file_path}': the specified path is an existing directory. Use Bash ls to list files in this directory, or specify a filename path to edit a specific file.`,
errorCode: 10,
}
}
if (fileStat.size > MAX_EDIT_FILE_SIZE) {
return {
result: false,
behavior: 'ask',
message: `File is too large to edit (${formatFileSize(fileStat.size)}). Maximum editable file size is ${formatFileSize(MAX_EDIT_FILE_SIZE)}.`,
errorCode: 10,
}
if (fileStat.isDirectory()) {
return {
result: false,
behavior: 'ask',
message: `Cannot edit '${file_path}': the specified path is an existing directory. Use Bash ls to list files in this directory, or specify a filename path to edit a specific file.`,
errorCode: 10,
}
}
if (fileStat.size > MAX_EDIT_FILE_SIZE) {
return {
result: false,
behavior: 'ask',
message: `File is too large to edit (${formatFileSize(fileStat.size)}). Maximum editable file size is ${formatFileSize(MAX_EDIT_FILE_SIZE)}.`,
errorCode: 11,
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/builtin-tools/src/tools/FileEditTool/FileEditTool.ts` around lines
188 - 202, The directory validation branch in the FileEditTool path checks
reuses errorCode 10 already assigned to the oversized-file branch. Update the
directory case to use a distinct error code while preserving the existing
file-size error code and all other response fields.

}
Expand Down
3 changes: 2 additions & 1 deletion packages/builtin-tools/src/tools/FileEditTool/prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ Usage:${getPreReadInstruction()}
- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.
- Only use emojis if the user explicitly requests it. Avoid adding emojis to files unless asked.
- The edit will FAIL if \`old_string\` is not unique in the file. Either provide a larger string with more surrounding context to make it unique or use \`replace_all\` to change every instance of \`old_string\`.${minimalUniquenessHint}
- Use \`replace_all\` for replacing and renaming strings across the file. This parameter is useful if you want to rename a variable for instance.`
- Use \`replace_all\` for replacing and renaming strings across the file. This parameter is useful if you want to rename a variable for instance.
- The file_path must be a file path, not a directory path. If the path resolves to an existing directory, the tool will reject it. Use a path that points to an existing file.`
}
17 changes: 17 additions & 0 deletions packages/builtin-tools/src/tools/FileReadTool/FileReadTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,23 @@ export const FileReadTool = buildTool({
return { result: true }
}

// 预检:如果路径已是一个存在的目录,及时拒绝并给出指引
// 避免 AI 模型用 Read 工具读取目录路径时抛出原始 EISDIR 错误,
// 模型拿到错误后不知该用 ls 列目录,可能做出错误恢复决策。
try {
const fileStat = await getFsImplementation().stat(fullFilePath)
if (fileStat.isDirectory()) {
return {
result: false,
message: `Cannot read '${file_path}': the specified path is an existing directory. Use Bash ls to list files in this directory, or specify a filename path to read a specific file.`,
errorCode: 10,
}
}
} catch (e) {
if (!isENOENT(e)) throw e
// ENOENT = 文件还不存在,放行,call() 会通过 findSimilarFile 给出友好建议
}

// Binary extension check (string check on extension only, no I/O).
// PDF, images, and SVG are excluded - this tool renders them natively.
const ext = path.extname(fullFilePath).toLowerCase()
Expand Down
11 changes: 11 additions & 0 deletions packages/builtin-tools/src/tools/FileWriteTool/FileWriteTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,17 @@ export const FileWriteTool = buildTool({
let fileMtimeMs: number
try {
const fileStat = await fs.stat(fullFilePath)
// 预检:如果路径已是一个存在的目录,及时拒绝并给出指引
// 避免 AI 模型传入目录路径(如 my-docs/analysis/api/)后,
// Write 工具先 mkdir 再写文件时抛出原始 EISDIR 错误,
// 导致模型不知如何处理、可能误删目录内容。
if (fileStat.isDirectory()) {
return {
result: false,
message: `Cannot write to '${file_path}': the specified path is an existing directory. To write a file inside this directory, use a path that includes a filename with extension, e.g. '${file_path}/<filename>.md'. Use Bash ls to list existing files in this directory.`,
errorCode: 5,
}
}
fileMtimeMs = fileStat.mtimeMs
} catch (e) {
if (isENOENT(e)) {
Expand Down
3 changes: 2 additions & 1 deletion packages/builtin-tools/src/tools/FileWriteTool/prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ Usage:
- This tool will overwrite the existing file if there is one at the provided path.${getPreReadInstruction()}
- Prefer the Edit tool for modifying existing files \u2014 it only sends the diff. Only use this tool to create new files or for complete rewrites.
- NEVER create documentation files (*.md) or README files unless explicitly requested by the User.
- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.`
- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.
- The file_path must be a distinct file path, not a directory path. If the path resolves to an existing directory, the tool will reject it with a clear error message. Use a path that includes a filename with an appropriate extension (e.g., \`my-docs/analysis/api/report.md\`).`
}