refactor: keep plugin login policy out of core

This commit is contained in:
Peter Steinberger
2026-04-22 06:38:43 +01:00
parent ec8ea02bb7
commit d94a981a33
11 changed files with 11 additions and 59 deletions

View File

@@ -382,18 +382,6 @@
}
}
},
"whatsapp_login": {
"emoji": "🟢",
"title": "WhatsApp Login",
"actions": {
"start": {
"label": "start"
},
"wait": {
"label": "wait"
}
}
},
"discord": {
"emoji": "💬",
"title": "Discord",

View File

@@ -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 }) => {

View File

@@ -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 }) => {

View File

@@ -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",
);
});
});

View File

@@ -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");

View File

@@ -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",

View File

@@ -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", () => {

View File

@@ -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",

View File

@@ -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 () => {

View File

@@ -32,7 +32,6 @@ function wrapOwnerOnlyToolExecution(tool: AnyAgentTool, senderIsOwner: boolean):
}
const OWNER_ONLY_TOOL_APPROVAL_CLASS_FALLBACKS = new Map<string, OwnerOnlyToolApprovalClass>([
["whatsapp_login", "interactive"],
["cron", "control_plane"],
["gateway", "control_plane"],
["nodes", "exec_capable"],

View File

@@ -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;