Skip to content

fix(hooks): fire post_tool_call hook for built-in tools (closes #12922)#12928

Closed
wpsl5168 wants to merge 1 commit into
NousResearch:mainfrom
wpsl5168:fix/post-tool-call-hook-builtin-tools
Closed

fix(hooks): fire post_tool_call hook for built-in tools (closes #12922)#12928
wpsl5168 wants to merge 1 commit into
NousResearch:mainfrom
wpsl5168:fix/post-tool-call-hook-builtin-tools

Conversation

@wpsl5168

Copy link
Copy Markdown

Summary

Fix for #12922post_tool_call plugin hook was silently bypassed for built-in tools that short-circuit handle_function_call: todo, memory, session_search, clarify, delegate_task, context-engine helpers, and memory-provider tools.

This broke external memory backends (and any plugin auditing tool calls) that subscribe to post_tool_call to mirror agent state — memory operations in particular went completely unobserved.

Changes

model_tools.py

  • Add skip_post_tool_call_hook=False parameter to handle_function_call, mirroring the existing skip_pre_tool_call_hook escape hatch. Lets the agent loop tell the dispatcher "I will fire the hook myself".

run_agent.py

  • Introduce private helper _fire_post_tool_call_hook(...) that swallows hook exceptions (consistent with model_tools behaviour).
  • Invoke it from every built-in branch of _invoke_tool (concurrent path).
  • Invoke it in _execute_tool_calls_sequential for every short-circuited built-in branch:
    • todo, session_search, memory (agent-loop), clarify — fired right after function_result is set
    • delegate_task — fired in finally so failures still emit the hook
    • Memory-provider tools — fired in the existing finally block
  • Registry-routed tools (the else branch / handle_function_call path) keep firing the hook exactly once via the dispatcher — no double-fire.

Tests

5 new tests in tests/run_agent/test_run_agent.py:

  • Parametrised: _invoke_tool fires post_tool_call exactly once for todo, memory, clarify with correct tool_name/args/result/tool_call_id payload.
  • session_search with no DB still fires the hook on its error path.
  • web_search (registry-routed) fires the hook exactly once — guards against double-fire regression.

Full regression run: 296/296 passing in tests/run_agent/ + tests/test_model_tools.py.

Observed Impact

In our setup (custom OpenHippo memory plugin syncing Hermes' built-in memory tool calls into a SQLite-backed long-term store), this bug caused 0 of 22 real memory entries to sync. After the fix lands, plugins receive the hook for every memory mutation as documented.

Notes

…#12922)

The post_tool_call plugin hook was silently bypassed for built-in tools
that short-circuit handle_function_call: todo, memory, session_search,
clarify, delegate_task, context-engine helpers, and memory-provider
tools. Plugins relying on the hook (e.g. external memory backends syncing
state via memory_tool calls) never observed these tool invocations.

Fix:
- model_tools.handle_function_call: add skip_post_tool_call_hook
  parameter, mirroring the existing skip_pre_tool_call_hook escape hatch
- run_agent: introduce _fire_post_tool_call_hook helper and invoke it
  in every built-in branch of _invoke_tool (concurrent path) and in
  _execute_tool_calls_sequential, with finally blocks for delegate_task
  and memory provider tools so failures still emit the hook
- Registry-routed tools (else branch) keep firing the hook exactly once
  via handle_function_call - no double-fire

Tests (5 new, all pass):
- parametrised test confirms post_tool_call fires for todo/memory/clarify
- session_search with no DB still fires hook on error path
- web_search still fires hook exactly once (no duplication)
- 296/296 existing run_agent + model_tools tests still green

Closes NousResearch#12922
@kshitijk4poor

Copy link
Copy Markdown
Collaborator

Closing as obsolete — the centralized post_tool_call hook firing for built-in tools (todo, memory, clarify, delegate_task, memory-provider tools, etc.) is already on main.

model_tools.py:782-795 unconditionally fires post_tool_call after every tool dispatch through handle_function_call, regardless of whether it's a built-in or a registry tool. This is the centralized mechanism this PR was originally proposing.

Discovered while reviewing #22345 which originally cited this PR as a dependency — turns out the central firing was added in a later refactor and the dependency is satisfied.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent loop, run_agent.py, prompt builder comp/plugins Plugin system and bundled plugins P1 High — major feature broken, no workaround tool/memory Memory tool and memory providers type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: post_tool_call hook is not invoked for built-in tools (memory, todo, session_search, clarify, delegate_task)

3 participants