mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
fix(patch): catch silent persistence failures and escape-drift in tool-call transport (#12669)
Two hardening layers in the patch tool, triggered by a real silent failure in the previous session: (1) Post-write verification in patch_replace — after write_file succeeds, re-read the file and confirm the bytes on disk match the intended write. If not, return an error instead of the current success-with-diff. Catches silent persistence failures from any cause (backend FS oddities, stdin pipe truncation, concurrent task races, mount drift). (2) Escape-drift guard in fuzzy_find_and_replace — when a non-exact strategy matches and both old_string and new_string contain literal \' or \" sequences but the matched file region does not, reject the patch with a clear error pointing at the likely cause (tool-call serialization adding a spurious backslash around apostrophes/quotes). Exact matches bypass the guard, and legitimate edits that add or preserve escape sequences in files that already have them still work. Why: in a prior tool call, old_string was sent with \' where the file has ' (tool-call transport drift). The fuzzy matcher's block_anchor strategy matched anyway and produced a diff the tool reported as successful — but the file was never modified on disk. The agent moved on believing the edit landed when it hadn't. Tests: added TestPatchReplacePostWriteVerification (3 cases) and TestEscapeDriftGuard (6 cases). All pass, existing fuzzy match and file_operations tests unaffected.
This commit is contained in:
parent
db60c98276
commit
d2c2e34469
4 changed files with 254 additions and 2 deletions
|
|
@ -794,7 +794,24 @@ class ShellFileOperations(FileOperations):
|
|||
write_result = self.write_file(path, new_content)
|
||||
if write_result.error:
|
||||
return PatchResult(error=f"Failed to write changes: {write_result.error}")
|
||||
|
||||
|
||||
# Post-write verification — re-read the file and confirm the bytes we
|
||||
# intended to write actually landed. Catches silent persistence
|
||||
# failures (backend FS oddities, race with another task, truncated
|
||||
# pipe, etc.) that would otherwise return success-with-diff while the
|
||||
# file is unchanged on disk.
|
||||
verify_cmd = f"cat {self._escape_shell_arg(path)} 2>/dev/null"
|
||||
verify_result = self._exec(verify_cmd)
|
||||
if verify_result.exit_code != 0:
|
||||
return PatchResult(error=f"Post-write verification failed: could not re-read {path}")
|
||||
if verify_result.stdout != new_content:
|
||||
return PatchResult(error=(
|
||||
f"Post-write verification failed for {path}: on-disk content "
|
||||
f"differs from intended write "
|
||||
f"(wrote {len(new_content)} chars, read back {len(verify_result.stdout)}). "
|
||||
"The patch did not persist. Re-read the file and try again."
|
||||
))
|
||||
|
||||
# Generate diff
|
||||
diff = self._unified_diff(content, new_content, path)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue