Skip to content

Wire up after_tool_call hook in agent execution loop#6264

Closed
xampla wants to merge 1 commit intoopenclaw:mainfrom
xampla:fix/wire-tool-call-hooks
Closed

Wire up after_tool_call hook in agent execution loop#6264
xampla wants to merge 1 commit intoopenclaw:mainfrom
xampla:fix/wire-tool-call-hooks

Conversation

@xampla
Copy link

@xampla xampla commented Feb 1, 2026

Summary

Now that before_tool_call is implemented (#6570), this PR adds the complementary after_tool_call hook for post-execution telemetry.

Changes

  • Add pi-tools.hooks.ts: wraps tool execute methods to fire after_tool_call
  • Hook fires as fire-and-forget after tool returns
  • Provides: toolName, params, result, error, durationMs
  • Hook context includes agentId and sessionKey for correlation
  • Hook errors are caught and logged, never breaking tool execution
  • Tools are only wrapped when hooks are registered (zero overhead otherwise)

API Usage

Plugins can register after_tool_call handlers:

api.on("after_tool_call", (event, ctx) => {
  console.log(`Tool ${event.toolName} completed in ${event.durationMs}ms`);
  if (event.error) {
    console.error(`Tool failed: ${event.error}`);
  }
});

Related

@openclaw-barnacle openclaw-barnacle bot added the agents Agent runtime and tooling label Feb 1, 2026
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 file reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +52 to +57
const reason = beforeResult.blockReason ?? "Blocked by plugin hook";
log.debug(`before_tool_call: blocked ${toolName} — ${reason}`);
return {
content: [{ type: "text", text: `[Tool call blocked] ${reason}` }],
details: { blocked: true, reason },
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Tool-call hooks bypass tool_result_persist transcript guard

When before_tool_call blocks, the wrapper returns a synthetic tool result ([Tool call blocked] ...). That synthetic result currently won’t go through the existing tool_result_persist hook path (which is where plugins can redact/shape tool-result transcript content), because it’s created inside the tool wrapper rather than at the transcript-persist boundary. This matters when plugins rely on tool_result_persist to strip sensitive blockReason details or standardize tool-result messages; blocked calls would be an exception.

If the intent is that blocked tool calls should be treated like any other tool result for transcript policy/redaction, consider emitting them through the same path (or at least ensure the persist hook sees them as synthetic).

(Also applies to the identical block return in wrapToolDefinitionWithHooks.)

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/pi-tools.hooks.ts
Line: 52:57

Comment:
[P1] Tool-call hooks bypass `tool_result_persist` transcript guard

When `before_tool_call` blocks, the wrapper returns a synthetic tool result (`[Tool call blocked] ...`). That synthetic result currently won’t go through the existing `tool_result_persist` hook path (which is where plugins can redact/shape tool-result transcript content), because it’s created inside the tool wrapper rather than at the transcript-persist boundary. This matters when plugins rely on `tool_result_persist` to strip sensitive `blockReason` details or standardize tool-result messages; blocked calls would be an exception.

If the intent is that blocked tool calls should be treated like any other tool result for transcript policy/redaction, consider emitting them through the same path (or at least ensure the persist hook sees them as synthetic).

(Also applies to the identical block return in `wrapToolDefinitionWithHooks`.)

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +75 to +78
} catch (err) {
error = String(err);
throw err;
} finally {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P3] after_tool_call error string may be low-signal

In the catch block you set error = String(err) and then rethrow. For many thrown Errors, String(err) is just the message and drops the stack/type/context. If hooks are used for auditing/debugging, passing something like err instanceof Error ? err.stack ?? err.message : String(err) tends to be more actionable.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/pi-tools.hooks.ts
Line: 75:78

Comment:
[P3] `after_tool_call` error string may be low-signal

In the catch block you set `error = String(err)` and then rethrow. For many thrown Errors, `String(err)` is just the message and drops the stack/type/context. If hooks are used for auditing/debugging, passing something like `err instanceof Error ? err.stack ?? err.message : String(err)` tends to be more actionable.

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

@xampla xampla force-pushed the fix/wire-tool-call-hooks branch from 5343c58 to adad8bf Compare February 1, 2026 14:16
@xampla
Copy link
Author

xampla commented Feb 1, 2026

This PR fixes #5943 and #5513.

It supersedes #2340 (by @AhmedTheGeek) and the closed #2363, rebasing the work onto current main with fixes for:

  • Type compatibility (separate wrappers for AnyAgentTool and ToolDefinition)
  • Lint compliance (unbound method fix)

This also unblocks #6232 (Security & Access Control) by enabling plugins to intercept and block tool calls before execution.

@xampla xampla force-pushed the fix/wire-tool-call-hooks branch 2 times, most recently from c253cd5 to b6c0f7c Compare February 2, 2026 11:22
@fuushyn
Copy link

fuushyn commented Feb 2, 2026

following

Now that before_tool_call is implemented (openclaw#6570), this PR adds the
complementary after_tool_call hook for post-execution telemetry.

- Add pi-tools.hooks.ts: wraps tool execute methods to fire after_tool_call
- Hook fires as fire-and-forget after tool returns with timing data
- Provides: toolName, params, result, error, durationMs
- Hook context includes agentId and sessionKey for correlation
- Hook errors are caught and logged, never breaking tool execution
- Tools are only wrapped when hooks are actually registered (zero overhead)

This addresses another hook from openclaw#6535 (2 of 8 now working).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@xampla xampla force-pushed the fix/wire-tool-call-hooks branch from b6c0f7c to e0b6f84 Compare February 2, 2026 22:46
@xampla xampla changed the title Wire up before_tool_call and after_tool_call hooks in agent execution loop Wire up after_tool_call hook in agent execution loop Feb 2, 2026
@sebslight
Copy link
Member

Closing as duplicate of #10678. If this is incorrect, comment and we can reopen.

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

Labels

agents Agent runtime and tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants