From d94a981a334346888ae20c7952512ffc2b8aebfe Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Wed, 22 Apr 2026 06:38:43 +0100 Subject: [PATCH] refactor: keep plugin login policy out of core --- .../Sources/OpenClawKit/Resources/tool-display.json | 12 ------------ src/acp/approval-classifier.test.ts | 6 ------ src/acp/client.test.ts | 7 ------- ...i-embedded-helpers.sanitizeuserfacingtext.test.ts | 4 ++-- ...ng.test.ts => pi-tools.owner-only-gating.test.ts} | 9 +++++---- src/agents/pi-tools.policy.ts | 2 -- src/agents/tool-call-id.test.ts | 6 +++--- src/agents/tool-display-config.ts | 12 ------------ src/agents/tool-policy.test.ts | 9 +-------- src/agents/tool-policy.ts | 1 - src/security/dangerous-tools.ts | 2 -- 11 files changed, 11 insertions(+), 59 deletions(-) rename src/agents/{pi-tools.whatsapp-login-gating.test.ts => pi-tools.owner-only-gating.test.ts} (91%) diff --git a/apps/shared/OpenClawKit/Sources/OpenClawKit/Resources/tool-display.json b/apps/shared/OpenClawKit/Sources/OpenClawKit/Resources/tool-display.json index b8e699bc83b..4959109b9a1 100644 --- a/apps/shared/OpenClawKit/Sources/OpenClawKit/Resources/tool-display.json +++ b/apps/shared/OpenClawKit/Sources/OpenClawKit/Resources/tool-display.json @@ -382,18 +382,6 @@ } } }, - "whatsapp_login": { - "emoji": "🟢", - "title": "WhatsApp Login", - "actions": { - "start": { - "label": "start" - }, - "wait": { - "label": "wait" - } - } - }, "discord": { "emoji": "💬", "title": "Discord", diff --git a/src/acp/approval-classifier.test.ts b/src/acp/approval-classifier.test.ts index 5359db66c54..487a96aab90 100644 --- a/src/acp/approval-classifier.test.ts +++ b/src/acp/approval-classifier.test.ts @@ -83,12 +83,6 @@ describe("classifyAcpToolApproval", () => { expectedToolName: "nodes", expectedClass: "exec_capable", }, - { - title: "whatsapp_login: start", - rawInput: { name: "whatsapp_login" }, - expectedToolName: "whatsapp_login", - expectedClass: "interactive", - }, ] as const)( "classifies shared owner-only ACP backstops for $expectedToolName", ({ title, rawInput, expectedToolName, expectedClass }) => { diff --git a/src/acp/client.test.ts b/src/acp/client.test.ts index f8b1c08c312..6cee967f280 100644 --- a/src/acp/client.test.ts +++ b/src/acp/client.test.ts @@ -430,13 +430,6 @@ describe("resolvePermissionRequest", () => { action: "list", }, }, - { - toolName: "whatsapp_login", - title: "whatsapp_login: start", - rawInput: { - name: "whatsapp_login", - }, - }, ] as const)( "prompts for shared owner-only backstop tools: $toolName", async ({ toolName, title, rawInput }) => { diff --git a/src/agents/pi-embedded-helpers.sanitizeuserfacingtext.test.ts b/src/agents/pi-embedded-helpers.sanitizeuserfacingtext.test.ts index 3c88bda0af4..6e2469caa0c 100644 --- a/src/agents/pi-embedded-helpers.sanitizeuserfacingtext.test.ts +++ b/src/agents/pi-embedded-helpers.sanitizeuserfacingtext.test.ts @@ -389,8 +389,8 @@ describe("sanitizeToolCallId", () => { it("strips all non-alphanumeric characters", () => { expect(sanitizeToolCallId("call_abc-123", "strict")).toBe("callabc123"); expect(sanitizeToolCallId("call_abc|item:456", "strict")).toBe("callabcitem456"); - expect(sanitizeToolCallId("whatsapp_login_1768799841527_1", "strict")).toBe( - "whatsapplogin17687998415271", + expect(sanitizeToolCallId("plugin_login_1768799841527_1", "strict")).toBe( + "pluginlogin17687998415271", ); }); }); diff --git a/src/agents/pi-tools.whatsapp-login-gating.test.ts b/src/agents/pi-tools.owner-only-gating.test.ts similarity index 91% rename from src/agents/pi-tools.whatsapp-login-gating.test.ts rename to src/agents/pi-tools.owner-only-gating.test.ts index 214aa2c1197..57ebcbabecc 100644 --- a/src/agents/pi-tools.whatsapp-login-gating.test.ts +++ b/src/agents/pi-tools.owner-only-gating.test.ts @@ -9,10 +9,11 @@ vi.mock("./channel-tools.js", () => { name, description: `${name} stub`, parameters: { type: "object", properties: {} }, + ownerOnly: true, execute: vi.fn(), }); return { - listChannelAgentTools: () => [stubTool("whatsapp_login")], + listChannelAgentTools: () => [stubTool("plugin_login")], copyChannelAgentToolMeta: passthrough, getChannelAgentToolMeta: () => undefined, }; @@ -22,7 +23,7 @@ describe("owner-only tool gating", () => { it("removes owner-only tools for unauthorized senders", () => { const tools = createOpenClawCodingTools({ senderIsOwner: false }); const toolNames = tools.map((tool) => tool.name); - expect(toolNames).not.toContain("whatsapp_login"); + expect(toolNames).not.toContain("plugin_login"); expect(toolNames).not.toContain("cron"); expect(toolNames).not.toContain("gateway"); expect(toolNames).not.toContain("nodes"); @@ -31,7 +32,7 @@ describe("owner-only tool gating", () => { it("keeps owner-only tools for authorized senders", () => { const tools = createOpenClawCodingTools({ senderIsOwner: true }); const toolNames = tools.map((tool) => tool.name); - expect(toolNames).toContain("whatsapp_login"); + expect(toolNames).toContain("plugin_login"); expect(toolNames).toContain("cron"); expect(toolNames).toContain("gateway"); expect(toolNames).toContain("nodes"); @@ -46,7 +47,7 @@ describe("owner-only tool gating", () => { it("defaults to removing owner-only tools when owner status is unknown", () => { const tools = createOpenClawCodingTools(); const toolNames = tools.map((tool) => tool.name); - expect(toolNames).not.toContain("whatsapp_login"); + expect(toolNames).not.toContain("plugin_login"); expect(toolNames).not.toContain("cron"); expect(toolNames).not.toContain("gateway"); expect(toolNames).not.toContain("nodes"); diff --git a/src/agents/pi-tools.policy.ts b/src/agents/pi-tools.policy.ts index c11187513ae..fc4a55b9fd5 100644 --- a/src/agents/pi-tools.policy.ts +++ b/src/agents/pi-tools.policy.ts @@ -35,8 +35,6 @@ const SUBAGENT_TOOL_DENY_ALWAYS = [ // System admin - dangerous from subagent "gateway", "agents_list", - // Interactive setup - not a task - "whatsapp_login", // Status/scheduling - main agent coordinates "session_status", "cron", diff --git a/src/agents/tool-call-id.test.ts b/src/agents/tool-call-id.test.ts index e85f2e1cf32..74b9a744f8a 100644 --- a/src/agents/tool-call-id.test.ts +++ b/src/agents/tool-call-id.test.ts @@ -281,7 +281,7 @@ describe("sanitizeToolCallIdsForCloudCodeAssist", () => { content: [ { type: "toolCall", - id: "whatsapp_login_1768799841527_1", + id: "plugin_login_1768799841527_1", name: "login", arguments: {}, }, @@ -289,7 +289,7 @@ describe("sanitizeToolCallIdsForCloudCodeAssist", () => { }, { role: "toolResult", - toolCallId: "whatsapp_login_1768799841527_1", + toolCallId: "plugin_login_1768799841527_1", toolName: "login", content: [{ type: "text", text: "ok" }], }, @@ -298,7 +298,7 @@ describe("sanitizeToolCallIdsForCloudCodeAssist", () => { const out = sanitizeToolCallIdsForCloudCodeAssist(input, "strict"); expect(out).not.toBe(input); // Strict mode strips all non-alphanumeric characters - expectSingleToolCallRewrite(out, "whatsapplogin17687998415271", "strict"); + expectSingleToolCallRewrite(out, "pluginlogin17687998415271", "strict"); }); it("preserves native anthropic ids while sanitizing mixed-provider ids when requested", () => { diff --git a/src/agents/tool-display-config.ts b/src/agents/tool-display-config.ts index a8e85ac526d..f7d932fb5a8 100644 --- a/src/agents/tool-display-config.ts +++ b/src/agents/tool-display-config.ts @@ -264,18 +264,6 @@ export const TOOL_DISPLAY_CONFIG: ToolDisplayConfig = { }, }, }, - whatsapp_login: { - emoji: "🟢", - title: "WhatsApp Login", - actions: { - start: { - label: "start", - }, - wait: { - label: "wait", - }, - }, - }, discord: { emoji: "💬", title: "Discord", diff --git a/src/agents/tool-policy.test.ts b/src/agents/tool-policy.test.ts index cc1b0be5921..a5cf37ec20e 100644 --- a/src/agents/tool-policy.test.ts +++ b/src/agents/tool-policy.test.ts @@ -32,10 +32,6 @@ function createOwnerPolicyTools() { ownerOnly: true, execute: async () => ({ content: [], details: {} }) as any, }, - { - name: "whatsapp_login", - execute: async () => ({ content: [], details: {} }) as any, - }, ] as unknown as AnyAgentTool[]; } @@ -76,7 +72,6 @@ describe("tool-policy", () => { }); it("identifies owner-only tools", () => { - expect(isOwnerOnlyToolName("whatsapp_login")).toBe(true); expect(isOwnerOnlyToolName("cron")).toBe(true); expect(isOwnerOnlyToolName("gateway")).toBe(true); expect(isOwnerOnlyToolName("nodes")).toBe(true); @@ -84,7 +79,6 @@ describe("tool-policy", () => { }); it("exposes stable approval classes for shared owner-only fallbacks", () => { - expect(resolveOwnerOnlyToolApprovalClass("whatsapp_login")).toBe("interactive"); expect(resolveOwnerOnlyToolApprovalClass("cron")).toBe("control_plane"); expect(resolveOwnerOnlyToolApprovalClass("gateway")).toBe("control_plane"); expect(resolveOwnerOnlyToolApprovalClass("nodes")).toBe("exec_capable"); @@ -101,7 +95,6 @@ describe("tool-policy", () => { cron: "control_plane", gateway: "control_plane", nodes: "exec_capable", - whatsapp_login: "interactive", }); }); @@ -114,7 +107,7 @@ describe("tool-policy", () => { it("keeps owner-only tools for the owner sender", async () => { const tools = createOwnerPolicyTools(); const filtered = applyOwnerOnlyToolPolicy(tools, true); - expect(filtered.map((t) => t.name)).toEqual(["read", "cron", "gateway", "whatsapp_login"]); + expect(filtered.map((t) => t.name)).toEqual(["read", "cron", "gateway"]); }); it("honors ownerOnly metadata for custom tool names", async () => { diff --git a/src/agents/tool-policy.ts b/src/agents/tool-policy.ts index dd4da44ffb3..d0a186e81b2 100644 --- a/src/agents/tool-policy.ts +++ b/src/agents/tool-policy.ts @@ -32,7 +32,6 @@ function wrapOwnerOnlyToolExecution(tool: AnyAgentTool, senderIsOwner: boolean): } const OWNER_ONLY_TOOL_APPROVAL_CLASS_FALLBACKS = new Map([ - ["whatsapp_login", "interactive"], ["cron", "control_plane"], ["gateway", "control_plane"], ["nodes", "exec_capable"], diff --git a/src/security/dangerous-tools.ts b/src/security/dangerous-tools.ts index a51aacf0168..a36a5685670 100644 --- a/src/security/dangerous-tools.ts +++ b/src/security/dangerous-tools.ts @@ -31,6 +31,4 @@ export const DEFAULT_GATEWAY_HTTP_TOOL_DENY = [ "gateway", // Node command relay can reach system.run on paired hosts "nodes", - // Interactive setup — requires terminal QR scan, hangs on HTTP - "whatsapp_login", ] as const;