fix: dispatch before_tool_call and after_tool_call hooks from both tool execution paths (openclaw#15012) thanks @Patrick-Barletta

Verified:
- pnpm check

Co-authored-by: Patrick-Barletta <67929313+Patrick-Barletta@users.noreply.github.com>
This commit is contained in:
Patrick Barletta
2026-02-12 16:48:11 -08:00
committed by GitHub
parent da2d09f57a
commit d34138dfee
8 changed files with 107 additions and 29 deletions

View File

@@ -6,6 +6,7 @@ import type {
import type { ToolDefinition } from "@mariozechner/pi-coding-agent";
import type { ClientToolDefinition } from "./pi-embedded-runner/run/params.js";
import { logDebug, logError } from "../logger.js";
import { getGlobalHookRunner } from "../plugins/hook-runner-global.js";
import { isPlainObject } from "../utils.js";
import { runBeforeToolCallHook } from "./pi-tools.before-tool-call.js";
import { normalizeToolName } from "./tool-policy.js";
@@ -90,7 +91,38 @@ export function toToolDefinitions(tools: AnyAgentTool[]): ToolDefinition[] {
execute: async (...args: ToolExecuteArgs): Promise<AgentToolResult<unknown>> => {
const { toolCallId, params, onUpdate, signal } = splitToolExecuteArgs(args);
try {
return await tool.execute(toolCallId, params, signal, onUpdate);
// Call before_tool_call hook
const hookOutcome = await runBeforeToolCallHook({
toolName: name,
params,
toolCallId,
});
if (hookOutcome.blocked) {
throw new Error(hookOutcome.reason);
}
const adjustedParams = hookOutcome.params;
const result = await tool.execute(toolCallId, adjustedParams, signal, onUpdate);
// Call after_tool_call hook
const hookRunner = getGlobalHookRunner();
if (hookRunner?.hasHooks("after_tool_call")) {
try {
await hookRunner.runAfterToolCall(
{
toolName: name,
params: isPlainObject(adjustedParams) ? adjustedParams : {},
result,
},
{ toolName: name },
);
} catch (hookErr) {
logDebug(
`after_tool_call hook failed: tool=${normalizedName} error=${String(hookErr)}`,
);
}
}
return result;
} catch (err) {
if (signal?.aborted) {
throw err;
@@ -107,11 +139,33 @@ export function toToolDefinitions(tools: AnyAgentTool[]): ToolDefinition[] {
logDebug(`tools: ${normalizedName} failed stack:\n${described.stack}`);
}
logError(`[tools] ${normalizedName} failed: ${described.message}`);
return jsonResult({
const errorResult = jsonResult({
status: "error",
tool: normalizedName,
error: described.message,
});
// Call after_tool_call hook for errors too
const hookRunner = getGlobalHookRunner();
if (hookRunner?.hasHooks("after_tool_call")) {
try {
await hookRunner.runAfterToolCall(
{
toolName: normalizedName,
params: isPlainObject(params) ? params : {},
error: described.message,
},
{ toolName: normalizedName },
);
} catch (hookErr) {
logDebug(
`after_tool_call hook failed: tool=${normalizedName} error=${String(hookErr)}`,
);
}
}
return errorResult;
}
},
} satisfies ToolDefinition;