← Back to PRs

#10678: feat(hooks): wire after_tool_call hook into tool execution pipeline

by yassinebkr open 2026-02-06 20:59 View on GitHub →
agents stale
# feat(plugins): add `after_tool_call` hook ## Summary Adds the `after_tool_call` plugin hook — the complement to `before_tool_call` (merged in #5513). Together they form the complete tool lifecycle pair for plugin observability. ## What this PR does - **New file: `pi-tools.after-tool-call.ts`** — exports `runAfterToolCallHook()` and `wrapToolWithAfterToolCallHook()` - **New file: `pi-tools.after-tool-call.test.ts`** — 7 tests covering the full contract - **Wired into `pi-tools.ts`** — every tool now gets both `before_tool_call` and `after_tool_call` hooks applied ## Design Follows the exact same pattern as `before_tool_call` with key differences: | | `before_tool_call` | `after_tool_call` | |---|---|---| | **Timing** | Before tool execution | After tool execution (in `finally`) | | **Blocking** | Yes — can block/modify params | No — fire-and-forget | | **Receives** | `toolName`, `params` | `toolName`, `params`, `result`, `error`, `durationMs` | | **Hook errors** | Logged, tool proceeds | Logged, never crash tool execution | | **Zero-overhead** | `hasHooks` fast-path | `hasHooks` fast-path | ### Fire-and-forget semantics The hook promise is deliberately **not awaited** — `runAfterToolCallHook` returns `void` (not `Promise<void>`). This ensures: - Tool results are returned immediately to the model - Slow hooks don't degrade agent responsiveness - Async hook errors are caught via `.catch()` on the promise ### Error resilience Three layers of protection: 1. `hasHooks` check — zero overhead when no plugins register `after_tool_call` 2. Synchronous `try/catch` around `hookRunner.runAfterToolCall()` call 3. `.catch()` on the returned promise for async failures ## Tests ``` ✓ executes tool normally when no hook is registered ✓ fires with correct event data after tool execution ✓ is fire-and-forget (does not block tool result) ✓ receives error info when tool execution fails ✓ continues execution when hook throws synchronously ✓ continues execution when hook rejects asynchronously ✓ normalizes non-object params for hook contract ``` ## References - Closes #7297 - Related: #5513 (`before_tool_call` implementation) <!-- greptile_comment --> <h2>Greptile Overview</h2> <h3>Greptile Summary</h3> This PR introduces an `after_tool_call` plugin hook and wires it into the tool wrapping pipeline so plugins can observe tool outcomes (result/error and duration) without blocking execution. It adds a new wrapper (`wrapToolWithAfterToolCallHook`) plus a vitest suite covering the hook contract, and updates `createOpenClawCodingTools` to apply both before/after tool-call hooks to every tool before abort-signal wrapping. <h3>Confidence Score: 3/5</h3> - This PR is close to mergeable but has a hook semantics issue that can mislead plugins. - Core implementation and tests look consistent with existing hook runner patterns, but the current wrapper order will emit `after_tool_call` events even when `before_tool_call` blocks a tool call, which is likely not the intended meaning of “after tool call/execution” for observability hooks. - src/agents/pi-tools.ts (hook wrapping order/semantics), src/agents/pi-tools.after-tool-call.ts (event semantics) <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs