Hermes Agent Deep Cuts: The `patch` Tool Is a 9-Strategy Fuzzy Engine
Pass an ASCII search string like title = "Hello World"--v1.0 against a Python file containing smart quotes and em-dashes (title = “Hello World”—v1.0). Instead of failing or stripping the typography down to ASCII, the patch tool updates the string while retaining the file’s native Unicode characters:
title = “Hello Universe”—v1.0
Now pass a tool-call argument with serialized backslashes (r"msg = \'hello world\'"). Instead of writing spurious backslashes directly into your source code, the engine halts execution and names the exact serialization failure:
Escape-drift detected: old_string and new_string contain the literal sequence "\'"
but the matched region of the file does not. This is almost always a tool-call
serialization artifact... Re-read the file with read_file and pass old_string/new_string
without backslash-escaping "'" characters.
The patch tool goes far beyond str.replace(). It handles the practical ways language models fail when editing code: indentation drift, space-versus-tab mismatch, escaped quotes, Unicode coercion, duplicate patch re-sends, and multi-file batch operations.
The mechanism: 9 matching strategies, escape drift, and Unicode preservation
When you invoke patch with mode="replace", Hermes hands the operation to fuzzy_find_and_replace() in tools/fuzzy_match.py. The engine evaluates nine distinct matching strategies in strict order. The first strategy that yields a match claims the edit:
exact(_strategy_exact): Direct string comparison against the file content.line_trimmed(_strategy_line_trimmed): Strips leading and trailing whitespace from every line before comparing.whitespace_normalized(_strategy_whitespace_normalized): Collapses multiple spaces and tabs into a single space.indentation_flexible(_strategy_indentation_flexible): Ignores indentation differences entirely during matching, then computes the indentation delta between the target file andold_stringto re-indentnew_stringautomatically through_reindent_replacement().escape_normalized(_strategy_escape_normalized): Converts literal two-character sequences like\ninto real newline control bytes.trimmed_boundary(_strategy_trimmed_boundary): Trims leading and trailing whitespace only on the first and last lines of the block, leaving internal indentation intact.unicode_normalized(_strategy_unicode_normalized): Normalizes smart single and double quotes, em and en dashes, non-breaking spaces, and CJK ideographic spaces to standard ASCII for search purposes. When this strategy matches,_preserve_unicode_in_replacement()aligns the replacement text with the target file’s original Unicode characters so unchanged lines keep their native typography.block_anchor(_strategy_block_anchor): Matches the first and last lines of a multi-line block, then usesdifflib.SequenceMatcherto locate the internal body.context_aware(_strategy_context_aware): Computes line-by-line similarity across the file using a 50% match threshold.
Beyond the search chain, four main guards govern how edits land:
1. Duplicate edit recovery (is_already_applied)
Before running search strategies, is_already_applied() checks whether new_string already exists verbatim in the file while old_string is gone. When an agent gets stuck in a retry loop and sends the same edit twice, Hermes returns a successful edit result instead of throwing a match failure.
2. Escape-drift protection (_detect_escape_drift)
Models calling JSON APIs often escape single quotes (\') or double quotes (\"). If strategy_name is not exact and new_string contains escaped quotes that do not exist in the matched file region, _detect_escape_drift() blocks the write. This prevents models from injecting literal backslashes into source code.
3. Control byte unescaping (_maybe_unescape_new_string)
JSON tool arguments frequently turn tabs and carriage returns into \t and \r. Hermes unescapes these characters into real control bytes only if the target file region already contains real tabs or carriage returns. If the file contains explicit backslash-letter sequences in source code literals, Hermes leaves new_string unescaped.
4. Similarity strategy restriction (_SIMILARITY_STRATEGIES)
Strategies 8 (block_anchor) and 9 (context_aware) rely on approximate matching. If replace_all=True is requested, Hermes explicitly rejects similarity strategies. Mass-replacing approximate matches across an entire file risks rewriting unrelated functions.
Multi-file V4A patch mode
When passed mode="patch", Hermes hands the payload to parse_v4a_patch() in tools/patch_parser.py. The V4A parser handles multi-file atomic edits containing four operation types in a single patch block:
*** Begin Patch
*** Update File: src/config.py
@@ update timeout @@
-TIMEOUT = 30
+TIMEOUT = 60
*** Add File: src/utils.py
+def ping():
+ return True
*** Delete File: src/legacy.py
*** Move File: src/old_name.py -> src/new_name.py
*** End Patch
apply_v4a_operations() executes all file additions, updates, deletions, and moves in sequence. If any hunk update fails to match, the operation fails before changing remaining files.
Post-patch syntax verification
After a replace or V4A patch write completes, file_operations.py executes in-process syntax checks (_lint_python_inproc, _lint_json_inproc, _lint_yaml_inproc, _lint_toml_inproc). If a patch introduces a Python SyntaxError or invalid JSON syntax, Hermes appends a diagnostic warning to the tool response so the agent can correct the edit immediately.
Advanced usage: exact commands and multi-file patches
Most operators call patch with single-line replacements. The real power comes from structural edits and V4A multi-file patches.
Replace mode with indentation relaxation
Suppose a target file uses 4-space indentation:
class Worker:
def execute(self):
print("running task")
If an agent sends 2-space indentation in old_string and new_string:
{
"mode": "replace",
"path": "worker.py",
"old_string": "class Worker:\n def execute(self):\n print(\"running task\")",
"new_string": "class Worker:\n def execute(self):\n print(\"task completed\")"
}
Strategy 4 (indentation_flexible) matches the target region, detects the 2-space to 4-space delta, and re-indents new_string to match the target file:
class Worker:
def execute(self):
print("task completed")
V4A multi-file patch execution
To create, update, and move files atomically in one tool invocation:
{
"mode": "patch",
"patch": "*** Begin Patch\n*** Update File: app/server.py\n@@ update port @@\n-PORT = 8080\n+PORT = 9090\n*** Add File: app/health.py\n+def check():\n+ return {'status': 'ok'}\n*** Move File: app/old_service.py -> app/service.py\n*** End Patch"
}
Programmatic Python usage
You can invoke the fuzzy matching engine directly in custom Hermes skills or plugins:
from tools.fuzzy_match import fuzzy_find_and_replace
content = "def calculate(a, b):\n return a + b\n"
old_text = "def calculate(a, b):\n return a + b"
new_text = "def calculate(a, b):\n return a * b"
new_content, count, strategy, error = fuzzy_find_and_replace(
content, old_text, new_text, replace_all=False
)
if error:
print(f"Patch failed: {error}")
else:
print(f"Applied 1 replacement using strategy '{strategy}'")
The gotcha: escape drift and ambiguity errors
The most common failure mode with patch happens when an edit contains valid backslashes in both old_string and new_string, but strategy 1 (exact) fails to match because of minor whitespace differences.
When a non-exact strategy matches, Hermes runs _detect_escape_drift(). If your old_string contains \' and the file region actually contains ', Hermes aborts with an Escape-drift detected error. The fix is to pass raw, unescaped quotes in old_string and new_string so strategy 1 (exact) or strategy 2 (line_trimmed) matches without triggering the escape-drift safety guard.
The second gotcha concerns replace_all=True. If your target string appears multiple times in a file and Hermes falls through to strategy 8 (block_anchor) or strategy 9 (context_aware), execution fails with:
Found 2 approximate matches via the 'context_aware' strategy; replace_all only applies to exact matches.
Similarity strategies cannot be combined with mass replacement. When replace_all=True is enabled, you must provide enough surrounding context lines for an exact or line-trimmed match to succeed.
Finally, old_string cannot be whitespace-only. Passing blank lines or runs of spaces returns an explicit rejection: old_string is only whitespace — provide non-blank text to match.
How to verify it is working
Verify the fuzzy engine and V4A patch parser using short Python test snippets:
1. Test strategy fallback and Unicode preservation
Run a test script that passes ASCII quotes against a file containing smart quotes:
python3 -c "
from tools import fuzzy_match
content = 'title = “Hello World”—v1.0\n'
old_str = 'title = \"Hello World\"--v1.0'
new_str = 'title = \"Hello Universe\"--v1.0'
new_content, count, strategy, error = fuzzy_find_and_replace(content, old_str, new_str)
print('Strategy:', strategy)
print('Output:', repr(new_content))
"
Expected output: Strategy: unicode_normalized and Output: 'title = “Hello Universe”—v1.0\n'.
2. Verify escape drift detection
Test that serialized backslashes in quotes trigger the escape drift guard:
python3 -c "
from tools import fuzzy_match
content = \"msg = 'hello world'\n\"
old_str = r\"msg = \'hello world\'\"
new_str = r\"msg = \'hello universe\'\"
_, count, _, error = fuzzy_find_and_replace(content, old_str, new_str)
print('Match count:', count)
print('Error:', error[:60] + '...')
"
Expected output: Match count: 0 and Error: Escape-drift detected: old_string and new_string contain....
3. Verify V4A multi-file patch parsing
Test V4A patch parsing across multiple operations:
python3 -c "
from tools.patch_parser import parse_v4a_patch
patch = '''*** Begin Patch
*** Update File: main.py
@@ context @@
-v = 1
+v = 2
*** Add File: helper.py
+x = 10
*** End Patch'''
ops, error = parse_v4a_patch(patch)
print('Parsed operations:', len(ops))
print('Op types:', [op.operation.value for op in ops])
"
Expected output: Parsed operations: 2 and Op types: ['update', 'add'].
Facts, inference, and open questions
Observed (installed Hermes Agent v0.20.5 source and live Python verification runs): fuzzy_find_and_replace() evaluates nine strategies in order (exact, line_trimmed, whitespace_normalized, indentation_flexible, escape_normalized, trimmed_boundary, unicode_normalized, block_anchor, context_aware); _strategy_indentation_flexible re-indents new_string to match the target file through _reindent_replacement(); _strategy_unicode_normalized normalizes smart quotes, em-dashes, and Zs space-separator characters while _preserve_unicode_in_replacement() retains original Unicode characters in unchanged lines; is_already_applied() converts duplicate edit re-sends into quiet successes when new_string exists and old_string is absent; _detect_escape_drift() blocks non-exact matches containing spurious \' or \" sequences; _SIMILARITY_STRATEGIES (block_anchor, context_aware) refuse replace_all=True; parse_v4a_patch() parses multi-file updates, additions, deletions, and moves; file_operations.py executes in-process syntax checks (_lint_python_inproc, _lint_json_inproc, _lint_yaml_inproc, _lint_toml_inproc) post-write.
Inference: The 9-strategy chain and escape-drift guards exist because language models frequently alter indentation, collapse spaces, double-escape quotes, or convert smart quotes into ASCII when generating JSON payloads. Handling these variations in the tool layer prevents retries and eliminates file corruption.
Open questions: In-process linters currently cover Python, JSON, YAML, and TOML. Syntax checking for JavaScript, TypeScript, and Go relies on external CLI binaries if available on PATH, falling back silently when those tools are missing.
Sources
tools/fuzzy_match.py—fuzzy_find_and_replace(), 9-strategy fallback chain,is_already_applied(),_detect_escape_drift(),_preserve_unicode_in_replacement(),_reindent_replacement()tools/patch_parser.py—parse_v4a_patch(),apply_v4a_operations(),PatchOperation,OperationTypetools/file_operations.py— post-patch in-process syntax checks (_lint_python_inproc,_lint_json_inproc,_lint_yaml_inproc,_lint_toml_inproc)- Hermes Agent Tools Reference —
patchtool usage, replace mode vs V4A patch mode