mirror of
https://github.com/moltbot/moltbot.git
synced 2026-03-21 16:41:56 +00:00
97 lines
2.6 KiB
TypeScript
97 lines
2.6 KiB
TypeScript
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,
|
|
};
|