mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 05:01:23 +00:00
This commit is contained in:
committed by
GitHub
parent
6c6f1e9660
commit
8eb11bd304
96
src/agents/pi-tools.before-tool-call.ts
Normal file
96
src/agents/pi-tools.before-tool-call.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
import type { AnyAgentTool } from "./tools/common.js";
|
||||
import { createSubsystemLogger } from "../logging/subsystem.js";
|
||||
import { getGlobalHookRunner } from "../plugins/hook-runner-global.js";
|
||||
import { normalizeToolName } from "./tool-policy.js";
|
||||
|
||||
type HookContext = {
|
||||
agentId?: string;
|
||||
sessionKey?: string;
|
||||
};
|
||||
|
||||
type HookOutcome = { blocked: true; reason: string } | { blocked: false; params: unknown };
|
||||
|
||||
const log = createSubsystemLogger("agents/tools");
|
||||
|
||||
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === "object" && value !== null && !Array.isArray(value);
|
||||
}
|
||||
|
||||
export async function runBeforeToolCallHook(args: {
|
||||
toolName: string;
|
||||
params: unknown;
|
||||
toolCallId?: string;
|
||||
ctx?: HookContext;
|
||||
}): Promise<HookOutcome> {
|
||||
const hookRunner = getGlobalHookRunner();
|
||||
if (!hookRunner?.hasHooks("before_tool_call")) {
|
||||
return { blocked: false, params: args.params };
|
||||
}
|
||||
|
||||
const toolName = normalizeToolName(args.toolName || "tool");
|
||||
const params = args.params;
|
||||
try {
|
||||
const normalizedParams = isPlainObject(params) ? params : {};
|
||||
const hookResult = await hookRunner.runBeforeToolCall(
|
||||
{
|
||||
toolName,
|
||||
params: normalizedParams,
|
||||
},
|
||||
{
|
||||
toolName,
|
||||
agentId: args.ctx?.agentId,
|
||||
sessionKey: args.ctx?.sessionKey,
|
||||
},
|
||||
);
|
||||
|
||||
if (hookResult?.block) {
|
||||
return {
|
||||
blocked: true,
|
||||
reason: hookResult.blockReason || "Tool call blocked by plugin hook",
|
||||
};
|
||||
}
|
||||
|
||||
if (hookResult?.params && isPlainObject(hookResult.params)) {
|
||||
if (isPlainObject(params)) {
|
||||
return { blocked: false, params: { ...params, ...hookResult.params } };
|
||||
}
|
||||
return { blocked: false, params: hookResult.params };
|
||||
}
|
||||
} catch (err) {
|
||||
const toolCallId = args.toolCallId ? ` toolCallId=${args.toolCallId}` : "";
|
||||
log.warn(`before_tool_call hook failed: tool=${toolName}${toolCallId} error=${String(err)}`);
|
||||
}
|
||||
|
||||
return { blocked: false, params };
|
||||
}
|
||||
|
||||
export function wrapToolWithBeforeToolCallHook(
|
||||
tool: AnyAgentTool,
|
||||
ctx?: HookContext,
|
||||
): AnyAgentTool {
|
||||
const execute = tool.execute;
|
||||
if (!execute) {
|
||||
return tool;
|
||||
}
|
||||
const toolName = tool.name || "tool";
|
||||
return {
|
||||
...tool,
|
||||
execute: async (toolCallId, params, signal, onUpdate) => {
|
||||
const outcome = await runBeforeToolCallHook({
|
||||
toolName,
|
||||
params,
|
||||
toolCallId,
|
||||
ctx,
|
||||
});
|
||||
if (outcome.blocked) {
|
||||
throw new Error(outcome.reason);
|
||||
}
|
||||
return await execute(toolCallId, outcome.params, signal, onUpdate);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export const __testing = {
|
||||
runBeforeToolCallHook,
|
||||
isPlainObject,
|
||||
};
|
||||
Reference in New Issue
Block a user