Files
openclaw/src/gateway/server-methods/tools-invoke.ts
Josh Avant fbd330b7aa fix(channels): honor configured read target policies (#99905)
* fix(channels): enforce configured read targets

* test(channels): align policy checks with boundaries

* fix: bind channel reads to trusted turn context

* test: satisfy gateway lint

* fix: narrow message action channel imports

* fix(feishu): authorize message reads before provider access

* fix(slack): await reaction clear authorization

* fix(channels): align provider action contracts

* fix(matrix): read direct-room account data before sync

* fix(channels): reject unsupported attachment actions early

* fix: restore trusted operator conversation reads

* fix(matrix): authorize pin actions before provider reads

* fix: preserve trusted channel read workflows

* fix(discord): resolve current channel ids consistently

* fix(agents): preserve message action turn capability

* fix(plugins): enforce host-owned read provenance

* fix(channels): harden Teams and Discord read policy

* fix(channels): preserve exact-current action compatibility

* fix(imessage): authorize trusted current chat aliases

* fix(channels): preserve normalized current aliases

* fix(channels): preserve external current target aliases

* fix: reconcile channel policy with current main

* fix(discord): isolate DM read policy

* fix(channels): enforce provider read gates

* fix(gateway): await serialized message action identity tokens

* fix(ci): refresh channel protocol contracts
2026-07-10 22:29:37 -05:00

98 lines
3.0 KiB
TypeScript

// Tool invocation methods adapt gateway-visible tools to RPC callers with
// protocol-shaped success, approval-required, validation, and error payloads.
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
import {
ErrorCodes,
errorShape,
formatValidationErrors,
validateToolsInvokeParams,
type ToolsInvokeResult,
} from "../../../packages/gateway-protocol/src/index.js";
import { resolveGatewayConversationReadOrigin } from "../conversation-read-origin.js";
import { invokeGatewayTool } from "../tools-invoke-shared.js";
import type { GatewayRequestHandlers } from "./types.js";
/**
* RPC adapter for invoking gateway-visible tools from connected clients.
*/
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";
}
/** Handles `tools.invoke` with protocol-shaped success and failure payloads. */
export const toolsInvokeHandlers: GatewayRequestHandlers = {
"tools.invoke": async ({ params, respond, context, client }) => {
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: client?.connect?.scopes?.includes("operator.admin"),
clientCaps: client?.connect?.caps,
conversationReadOrigin: resolveGatewayConversationReadOrigin({
client,
requestedOrigin: params.conversationReadOrigin,
}),
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);
},
};