Compare commits

...

1 Commits

Author SHA1 Message Date
Aiden Cline 8abfd71684 refactor(apply_patch): avoid redundant file reads during update verification
Make patch derivation async and allow callers to provide existing file content so apply_patch update checks reuse already-loaded text instead of reading the same file twice.
2026-02-17 19:51:23 -06:00
2 changed files with 15 additions and 14 deletions
+14 -13
View File
@@ -1,7 +1,6 @@
import z from "zod" import z from "zod"
import * as path from "path" import * as path from "path"
import * as fs from "fs/promises" import * as fs from "fs/promises"
import { readFileSync } from "fs"
import { Log } from "../util/log" import { Log } from "../util/log"
export namespace Patch { export namespace Patch {
@@ -308,16 +307,18 @@ export namespace Patch {
content: string content: string
} }
export function deriveNewContentsFromChunks(filePath: string, chunks: UpdateFileChunk[]): ApplyPatchFileUpdate { export async function deriveNewContentsFromChunks(
// Read original file content filePath: string,
let originalContent: string chunks: UpdateFileChunk[],
try { originalContent?: string,
originalContent = readFileSync(filePath, "utf-8") ): Promise<ApplyPatchFileUpdate> {
} catch (error) { const content =
throw new Error(`Failed to read file ${filePath}: ${error}`) originalContent ??
} (await fs.readFile(filePath, "utf-8").catch((error) => {
throw new Error(`Failed to read file ${filePath}: ${error}`)
}))
let originalLines = originalContent.split("\n") let originalLines = content.split("\n")
// Drop trailing empty element for consistent line counting // Drop trailing empty element for consistent line counting
if (originalLines.length > 0 && originalLines[originalLines.length - 1] === "") { if (originalLines.length > 0 && originalLines[originalLines.length - 1] === "") {
@@ -335,7 +336,7 @@ export namespace Patch {
const newContent = newLines.join("\n") const newContent = newLines.join("\n")
// Generate unified diff // Generate unified diff
const unifiedDiff = generateUnifiedDiff(originalContent, newContent) const unifiedDiff = generateUnifiedDiff(content, newContent)
return { return {
unified_diff: unifiedDiff, unified_diff: unifiedDiff,
@@ -545,7 +546,7 @@ export namespace Patch {
break break
case "update": case "update":
const fileUpdate = deriveNewContentsFromChunks(hunk.path, hunk.chunks) const fileUpdate = await deriveNewContentsFromChunks(hunk.path, hunk.chunks)
if (hunk.move_path) { if (hunk.move_path) {
// Handle file move // Handle file move
@@ -641,7 +642,7 @@ export namespace Patch {
case "update": case "update":
const updatePath = path.resolve(effectiveCwd, hunk.path) const updatePath = path.resolve(effectiveCwd, hunk.path)
try { try {
const fileUpdate = deriveNewContentsFromChunks(updatePath, hunk.chunks) const fileUpdate = await deriveNewContentsFromChunks(updatePath, hunk.chunks)
changes.set(resolvedPath, { changes.set(resolvedPath, {
type: "update", type: "update",
unified_diff: fileUpdate.unified_diff, unified_diff: fileUpdate.unified_diff,
+1 -1
View File
@@ -101,7 +101,7 @@ export const ApplyPatchTool = Tool.define("apply_patch", {
// Apply the update chunks to get new content // Apply the update chunks to get new content
try { try {
const fileUpdate = Patch.deriveNewContentsFromChunks(filePath, hunk.chunks) const fileUpdate = await Patch.deriveNewContentsFromChunks(filePath, hunk.chunks, oldContent)
newContent = fileUpdate.content newContent = fileUpdate.content
} catch (error) { } catch (error) {
throw new Error(`apply_patch verification failed: ${error}`) throw new Error(`apply_patch verification failed: ${error}`)