mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 05:50:43 +00:00
Adds the SDK-facing tools.invoke Gateway RPC for #74705. Reuses the /tools/invoke policy path for tool policy, deny-list, owner filtering, before-tool-call hooks, session/agent scoping, and plugin approval handling. Returns typed SDK approval/refusal/success results while preserving HTTP compatibility and uses idempotencyKey as the stable tool-call id. Includes protocol schema exports, method scope/list registration, SDK helper/types, docs, generated Swift models, tests, and changelog credit.
87 lines
2.4 KiB
TypeScript
87 lines
2.4 KiB
TypeScript
import { normalizeOptionalString } from "../../shared/string-coerce.js";
|
|
import { ADMIN_SCOPE } from "../method-scopes.js";
|
|
import {
|
|
ErrorCodes,
|
|
errorShape,
|
|
formatValidationErrors,
|
|
validateToolsInvokeParams,
|
|
type ToolsInvokeResult,
|
|
} from "../protocol/index.js";
|
|
import { invokeGatewayTool } from "../tools-invoke-shared.js";
|
|
import type { GatewayRequestHandlers } from "./types.js";
|
|
|
|
function resolveRpcErrorCode(params: {
|
|
type: "invalid_request" | "not_found" | "tool_call_blocked" | "tool_error";
|
|
requiresApproval?: boolean;
|
|
}): string {
|
|
if (params.requiresApproval) {
|
|
return "requires_approval";
|
|
}
|
|
switch (params.type) {
|
|
case "invalid_request":
|
|
return "validation_error";
|
|
case "not_found":
|
|
return "not_found";
|
|
case "tool_call_blocked":
|
|
return "forbidden";
|
|
case "tool_error":
|
|
return "internal_error";
|
|
}
|
|
return "internal_error";
|
|
}
|
|
|
|
export const toolsInvokeHandlers: GatewayRequestHandlers = {
|
|
"tools.invoke": async ({ params, client, respond, context }) => {
|
|
if (!validateToolsInvokeParams(params)) {
|
|
respond(
|
|
false,
|
|
undefined,
|
|
errorShape(
|
|
ErrorCodes.INVALID_REQUEST,
|
|
`invalid tools.invoke params: ${formatValidationErrors(validateToolsInvokeParams.errors)}`,
|
|
),
|
|
);
|
|
return;
|
|
}
|
|
const requestedToolName = normalizeOptionalString(params.name);
|
|
if (!requestedToolName) {
|
|
respond(
|
|
false,
|
|
undefined,
|
|
errorShape(ErrorCodes.INVALID_REQUEST, "invalid tools.invoke params: name required"),
|
|
);
|
|
return;
|
|
}
|
|
|
|
const outcome = await invokeGatewayTool({
|
|
cfg: context.getRuntimeConfig(),
|
|
input: params,
|
|
senderIsOwner: Boolean(client?.connect.scopes?.includes(ADMIN_SCOPE)),
|
|
toolCallIdPrefix: "rpc",
|
|
approvalMode: params.confirm === true ? "request" : "report",
|
|
});
|
|
|
|
if (outcome.ok) {
|
|
const payload: ToolsInvokeResult = {
|
|
ok: true,
|
|
toolName: outcome.toolName,
|
|
output: outcome.result,
|
|
source: outcome.source,
|
|
};
|
|
respond(true, payload, undefined);
|
|
return;
|
|
}
|
|
|
|
const payload: ToolsInvokeResult = {
|
|
ok: false,
|
|
toolName: outcome.toolName || requestedToolName,
|
|
...(outcome.error.requiresApproval ? { requiresApproval: true } : {}),
|
|
error: {
|
|
code: resolveRpcErrorCode(outcome.error),
|
|
message: outcome.error.message,
|
|
},
|
|
};
|
|
respond(true, payload, undefined);
|
|
},
|
|
};
|