




















@@ -7,6 +7,10 @@ type RunBeforeToolCallHook = typeof runBeforeToolCallHookType;
77type RunBeforeToolCallHookArgs = Parameters<RunBeforeToolCallHook>[0];
88type RunBeforeToolCallHookResult = Awaited<ReturnType<RunBeforeToolCallHook>>;
9910+const pluginToolMetaState = vi.hoisted(
11+() => new Map<string, { pluginId: string; optional: boolean }>(),
12+);
13+1014const hookMocks = vi.hoisted(() => ({
1115resolveToolLoopDetectionConfig: vi.fn(() => ({ warnAt: 3 })),
1216runBeforeToolCallHook: vi.fn(
@@ -63,7 +67,8 @@ vi.mock("../plugins/config-state.js", async (importOriginal) => {
6367});
64686569vi.mock("../plugins/tools.js", () => ({
66-getPluginToolMeta: () => undefined,
70+getPluginToolMeta: (tool: { name?: string }) =>
71+typeof tool?.name === "string" ? pluginToolMetaState.get(tool.name) : undefined,
6772}));
68736974// Perf: the real tool factory instantiates many tools per request; for these HTTP
@@ -136,6 +141,11 @@ vi.mock("../agents/openclaw-tools.js", () => {
136141parameters: { type: "object", properties: {} },
137142execute: async () => ({ ok: true, result: "browser" }),
138143},
144+{
145+name: "plugin_doctor",
146+parameters: { type: "object", properties: {} },
147+execute: async () => ({ ok: true, permissionFlow: true }),
148+},
139149{
140150name: "owner_only_test",
141151ownerOnly: true,
@@ -259,6 +269,8 @@ beforeEach(() => {
259269pluginHttpHandlers = [];
260270cfg = {};
261271lastCreateOpenClawToolsContext = undefined;
272+pluginToolMetaState.clear();
273+pluginToolMetaState.set("plugin_doctor", { pluginId: "test-plugin", optional: true });
262274hookMocks.resolveToolLoopDetectionConfig.mockClear();
263275hookMocks.resolveToolLoopDetectionConfig.mockImplementation(() => ({ warnAt: 3 }));
264276hookMocks.runBeforeToolCallHook.mockClear();
@@ -463,6 +475,25 @@ describe("POST /tools/invoke", () => {
463475expect(lastCreateOpenClawToolsContext?.disablePluginTools).toBe(false);
464476});
465477478+it("allows the requested plugin tool through Gateway profile filtering", async () => {
479+cfg = {
480+ ...cfg,
481+agents: { list: [{ id: "main", default: true }] },
482+tools: { profile: "minimal" },
483+};
484+485+const res = await invokeToolAuthed({
486+tool: "plugin_doctor",
487+sessionKey: "main",
488+});
489+490+const body = await expectOkInvokeResponse(res);
491+expect(body.result).toMatchObject({ ok: true, permissionFlow: true });
492+expect(lastCreateOpenClawToolsContext?.pluginToolAllowlist).toEqual(
493+expect.arrayContaining(["plugin_doctor"]),
494+);
495+});
496+466497it("blocks tool execution when before_tool_call rejects the invoke", async () => {
467498setMainAllowedTools({ allow: ["tools_invoke_test"] });
468499hookMocks.runBeforeToolCallHook.mockResolvedValueOnce({
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。