feat(hooks): add BeforeToolCallback to intercept tool calls before execution#533
Open
ousamabenyounes wants to merge 2 commits intomistralai:mainfrom
Open
feat(hooks): add BeforeToolCallback to intercept tool calls before execution#533ousamabenyounes wants to merge 2 commits intomistralai:mainfrom
ousamabenyounes wants to merge 2 commits intomistralai:mainfrom
Conversation
Introduces a `before_tool_callback` mechanism on `AgentLoop` that lets callers intercept every tool invocation before execution and optionally rewrite the arguments. The callback signature is: async def cb(tool_name: str, args: dict) -> dict | None Returning `None` leaves the args unchanged; returning a modified dict replaces the args passed to the tool. This enables transparent command rewriting (e.g. `git status` → `rtk git status`) without patching any tool internals. Changes: - `types.py`: add `BeforeToolCallback` type alias - `tools/base.py`: add `before_tool_callback` field to `InvokeContext` - `agent_loop.py`: apply callback before `tool_instance.invoke()`, expose `set_before_tool_callback()` public method (mirrors `set_approval_callback` pattern) Tests: 5 new tests covering passthrough, rewriting, and error-free execution with rewritten args. Closes mistralai#531
- args_dict is now shallow-copied before being passed to the callback so
the callback cannot mutate the frozen ResolvedToolCall internals
- callback return is merged with original args ({**original, **modified})
instead of replacing them wholesale; partial returns (only the changed
key) no longer silently drop the other fields
- exceptions raised by the callback are caught and logged as warnings;
the tool call continues with the original args (silent fallback)
- two new tests: partial-return merge and callback-exception fallback
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
Adds a
before_tool_callbackhook toAgentLoopthat lets callers intercept every tool invocation before execution and optionally rewrite the arguments.Motivation: External tools like RTK (Rust Token Killer) need to transparently rewrite shell commands before they run (e.g.
git status→rtk git status). Claude Code and Gemini CLI both provide equivalent hooks (PreToolUseandBeforeTool). This PR brings Vibe to parity. Tracked in rtk-ai/rtk#800.API
The callback receives
(tool_name, args_dict)and may:None→ args unchanged (passthrough)Changes
vibe/core/types.pyBeforeToolCallbacktype aliasvibe/core/tools/base.pybefore_tool_callbackfield inInvokeContextvibe/core/agent_loop.pyinvoke(),set_before_tool_callback()public methodtests/test_before_tool_callback.pyDesign notes
set_approval_callback/set_user_input_callbackpattern exactlyNonecheck guards every path)run()Test plan
test_callback_receives_tool_name_and_args— correct inputs to callbacktest_callback_none_leaves_args_unchanged— passthrough behaviourtest_callback_can_rewrite_args— rewrite reaches the tooltest_no_callback_tool_executes_normally— no regression without callbacktest_rewritten_args_produce_successful_tool_result— no errors with rewritten args🤖 Generated with Claude Code