mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 09:10:45 +00:00
fix(gateway): invoke plugin-backed catalog tools
Co-authored-by: chat2way <chat2way@users.noreply.github.com>
This commit is contained in:
@@ -70,6 +70,7 @@ Docs: https://docs.openclaw.ai
|
||||
### Fixes
|
||||
|
||||
- Subagents: stop stale unended runs from counting as active or pending forever, while preserving restart-aborted recovery for recoverable child sessions. Fixes #71252. Thanks @hclsys.
|
||||
- Gateway/tools: allow `POST /tools/invoke` to reach plugin-backed catalog tools such as `browser` when no core implementation exists, while still preferring built-in tools for real core names. Thanks @chat2way.
|
||||
- Browser/security: require `operator.admin` for the `browser.request` gateway method, matching the host/browser-node control authority exposed by that route. Thanks @RichardCao.
|
||||
- Reply media: allow sandboxed replies to deliver OpenClaw-managed `media/outbound` and `media/tool-*` attachments without treating them as sandbox escapes, while keeping alias-escape checks on the managed media root. Fixes #71138. Thanks @mayor686, @truffle-dev, and @neeravmakwana.
|
||||
- CLI/agent: keep `openclaw agent --json` stdout reserved for the JSON response by routing gateway, plugin, and embedded-fallback diagnostics to stderr before execution starts. Fixes #71319.
|
||||
|
||||
@@ -127,6 +127,11 @@ vi.mock("../agents/openclaw-tools.js", () => {
|
||||
parameters: { type: "object", properties: {} },
|
||||
execute: async () => ({ ok: true, result: "nodes" }),
|
||||
},
|
||||
{
|
||||
name: "browser",
|
||||
parameters: { type: "object", properties: {} },
|
||||
execute: async () => ({ ok: true, result: "browser" }),
|
||||
},
|
||||
{
|
||||
name: "owner_only_test",
|
||||
ownerOnly: true,
|
||||
@@ -181,7 +186,7 @@ vi.mock("../agents/openclaw-tools.js", () => {
|
||||
return {
|
||||
createOpenClawTools: (ctx: Record<string, unknown>) => {
|
||||
lastCreateOpenClawToolsContext = ctx;
|
||||
return tools;
|
||||
return ctx.disablePluginTools ? tools.filter((tool) => tool.name !== "browser") : tools;
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -878,4 +883,17 @@ describe("POST /tools/invoke", () => {
|
||||
expect(nodesRes.status).toBe(404);
|
||||
expect(nodesAdminRes.status).toBe(404);
|
||||
});
|
||||
|
||||
it("falls back to plugin-backed tools when a cataloged core tool has no core implementation", async () => {
|
||||
setMainAllowedTools({ allow: ["browser"] });
|
||||
|
||||
const res = await invokeToolAuthed({
|
||||
tool: "browser",
|
||||
sessionKey: "main",
|
||||
});
|
||||
|
||||
const body = await expectOkInvokeResponse(res);
|
||||
expect(body.result).toEqual({ ok: true, result: "browser" });
|
||||
expect(lastCreateOpenClawToolsContext?.disablePluginTools).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -226,19 +226,25 @@ export async function handleToolsInvokeHttpRequest(
|
||||
// with the correct owner context and channel-action gates (e.g. Matrix set-profile)
|
||||
// work correctly for both owner and non-owner callers.
|
||||
const senderIsOwner = resolveOpenAiCompatibleHttpSenderIsOwner(req, requestAuth);
|
||||
const { agentId, tools } = resolveGatewayScopedTools({
|
||||
cfg,
|
||||
sessionKey,
|
||||
messageProvider: messageChannel ?? undefined,
|
||||
accountId,
|
||||
agentTo,
|
||||
agentThreadId,
|
||||
allowGatewaySubagentBinding: true,
|
||||
allowMediaInvokeCommands: true,
|
||||
surface: "http",
|
||||
disablePluginTools: isKnownCoreToolId(toolName),
|
||||
senderIsOwner,
|
||||
});
|
||||
const resolveTools = (disablePluginTools: boolean) =>
|
||||
resolveGatewayScopedTools({
|
||||
cfg,
|
||||
sessionKey,
|
||||
messageProvider: messageChannel ?? undefined,
|
||||
accountId,
|
||||
agentTo,
|
||||
agentThreadId,
|
||||
allowGatewaySubagentBinding: true,
|
||||
allowMediaInvokeCommands: true,
|
||||
surface: "http",
|
||||
disablePluginTools,
|
||||
senderIsOwner,
|
||||
});
|
||||
const knownCoreTool = isKnownCoreToolId(toolName);
|
||||
let { agentId, tools } = resolveTools(knownCoreTool);
|
||||
if (knownCoreTool && !tools.some((candidate) => candidate.name === toolName)) {
|
||||
({ agentId, tools } = resolveTools(false));
|
||||
}
|
||||
const gatewayFiltered = applyOwnerOnlyToolPolicy(tools, senderIsOwner);
|
||||
|
||||
const tool = gatewayFiltered.find((t) => t.name === toolName);
|
||||
|
||||
Reference in New Issue
Block a user