@@ -156,6 +156,33 @@ describe("edit tool recovery hardening", () => {
|
156 | 156 | expectRecoveredText(result, `Successfully replaced text in ${filePath}.`); |
157 | 157 | }); |
158 | 158 | |
| 159 | +it("recovers post-write failures when edit calls use file_path", async () => { |
| 160 | +tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-edit-recovery-")); |
| 161 | +const workspaceDir = path.join(tmpDir, ".openclaw", "workspace"); |
| 162 | +await fs.mkdir(workspaceDir, { recursive: true }); |
| 163 | +const filePath = path.join(workspaceDir, "AGENTS.md"); |
| 164 | +await fs.writeFile(filePath, "# Agent\nold instruction\n", "utf-8"); |
| 165 | + |
| 166 | +const tool = createRecoveredEditTool({ |
| 167 | +root: tmpDir, |
| 168 | +readFile: (absolutePath) => fs.readFile(absolutePath, "utf-8"), |
| 169 | +execute: async () => { |
| 170 | +await fs.writeFile(filePath, "# Agent\nnew instruction\n", "utf-8"); |
| 171 | +throw new Error("Simulated post-write failure (e.g. generateDiffString)"); |
| 172 | +}, |
| 173 | +}); |
| 174 | +const result = await tool.execute( |
| 175 | +"call-1", |
| 176 | +{ |
| 177 | +file_path: filePath, |
| 178 | +edits: [{ oldText: "old instruction", newText: "new instruction" }], |
| 179 | +}, |
| 180 | +undefined, |
| 181 | +); |
| 182 | + |
| 183 | +expectRecoveredText(result, `Successfully replaced text in ${filePath}.`); |
| 184 | +}); |
| 185 | + |
159 | 186 | it("does not recover false success when the file never changed", async () => { |
160 | 187 | tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-edit-recovery-")); |
161 | 188 | const filePath = path.join(tmpDir, "demo.txt"); |
|