Files
openclaw/src/plugins/trusted-tool-policy.ts
EVA 1adaa28dc8 [plugin sdk] Add generic plugin host-hook contracts (#72287)
Merged via squash.

Prepared head SHA: 68e5f2ce19
Co-authored-by: 100yenadmin <239388517+100yenadmin@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman
2026-04-27 17:07:02 -07:00

55 lines
2.0 KiB
TypeScript

import type {
PluginHookBeforeToolCallEvent,
PluginHookBeforeToolCallResult,
PluginHookToolContext,
} from "./hook-types.js";
import { getActivePluginRegistry } from "./runtime.js";
export async function runTrustedToolPolicies(
event: PluginHookBeforeToolCallEvent,
ctx: PluginHookToolContext,
): Promise<PluginHookBeforeToolCallResult | undefined> {
const policies = getActivePluginRegistry()?.trustedToolPolicies ?? [];
let adjustedParams = event.params;
let hasAdjustedParams = false;
let approval: PluginHookBeforeToolCallResult["requireApproval"];
for (const registration of policies) {
const decision = await registration.policy.evaluate({ ...event, params: adjustedParams }, ctx);
if (!decision) {
continue;
}
if ("allow" in decision && decision.allow === false) {
return {
block: true,
blockReason: decision.reason ?? `blocked by ${registration.policy.id}`,
};
}
// `block: true` is terminal; normalize a missing blockReason to a deterministic
// reason so downstream diagnostics match the `{ allow: false }` path above.
if ("block" in decision && decision.block === true) {
return {
...decision,
blockReason: decision.blockReason ?? `blocked by ${registration.policy.id}`,
};
}
// `block: false` is a no-op (matches the regular `before_tool_call` hook
// pipeline) — it does NOT short-circuit the policy chain. Params and
// approvals are remembered so later trusted policies can still inspect or
// block the final call.
if ("params" in decision && decision.params) {
adjustedParams = decision.params;
hasAdjustedParams = true;
}
if ("requireApproval" in decision && decision.requireApproval && !approval) {
approval = decision.requireApproval;
}
}
if (!hasAdjustedParams && !approval) {
return undefined;
}
return {
...(hasAdjustedParams ? { params: adjustedParams } : {}),
...(approval ? { requireApproval: approval } : {}),
};
}