feat(hooks): pre_tool_call content transformation via modify directive - #87482
Merged
Conversation
Adds a `modify` response type to pre_tool_call hooks so a hook can
transform tool arguments before the tool executes, instead of repairing
results afterwards via post_tool_call.
- hermes_cli/plugins.py: _dispatch_pre_tool_call_hooks() fires hooks once
and returns (block_message, modified_args); modify directives
shallow-merge into an accumulated dict built from the original args.
- agent/shell_hooks.py: _parse_response() accepts both the canonical
{"action": "modify", "args": {...}} and Claude Code-compatible
{"decision": "modify", "tool_input": {...}} wire formats.
- model_tools.py, agent/tool_executor.py, agent/agent_runtime_helpers.py:
dispatch sites migrated; modified args applied before execution.
- Docs + 10 new tests (merge semantics, precedence, block interplay).
Salvaged from PR #28953. Best fix for #18988.
Follow-up to the #28953 salvage: - Extract _resolve_block_from_details() so resolve_pre_tool_block and _dispatch_pre_tool_call_hooks share ONE fail-closed approval-gate implementation. This also gives the new dispatcher the observability context wrapping around request_tool_approval that the original PR's inlined copy lacked. - Update sibling tests that patched resolve_pre_tool_block at the three migrated dispatch sites to patch _dispatch_pre_tool_call_hooks with the (block_message, modified_args) tuple contract. Verified: 448 targeted tests green; E2E with a real shell hook in an isolated HERMES_HOME rewrote a live write_file call (path + content) through handle_function_call, with block and negative paths intact.
Contributor
૮ >ﻌ< ა ci reviewran on 033a11b — chore: contributor mapping for NikolaRHristov
|
This was referenced Aug 16, 2026
13 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
pre_tool_callhooks can now transform tool arguments before execution via amodifydirective, instead of repairing results afterwards throughpost_tool_call. Salvage of #28953 by @NikolaRHristov onto current main, with a follow-up refactor.Changes
hermes_cli/plugins.py:_dispatch_pre_tool_call_hooks()fires hooks once and returns(block_message, modified_args);modifydirectives shallow-merge into an accumulated dict (later hook wins per key). Follow-up: extracted_resolve_block_from_details()so the dispatcher andresolve_pre_tool_blockshare ONE fail-closed approval-gate implementation (this also gives the dispatcher the observability-context wrapping around the human-approval gate that the original inlined copy lacked).agent/shell_hooks.py:_parse_response()accepts both{"action": "modify", "args": {...}}and Claude Code-compatible{"decision": "modify", "tool_input": {...}}.model_tools.py,agent/tool_executor.py,agent/agent_runtime_helpers.py: dispatch sites migrated; modified args applied before execution. Single-fire hook contract preserved.Validation
register_from_config)write_filethroughhandle_function_callrewrote path DECOY→REWRITTEN + prepended banner; decoy never written; block + negative paths intactBest fix for #18988. Credit also to @elasticdotventures whose #19305 proposed the same capability earlier — this implementation was chosen as the cleaner of the two.
Infographic