diff --git a/src/agents/pi-tools-agent-config.test.ts b/src/agents/pi-tools-agent-config.test.ts new file mode 100644 index 000000000..0b8affd39 --- /dev/null +++ b/src/agents/pi-tools-agent-config.test.ts @@ -0,0 +1,207 @@ +import { describe, expect, it } from "vitest"; +import type { ClawdbotConfig } from "../config/config.js"; +import { createClawdbotCodingTools } from "./pi-tools.js"; + +describe("Agent-specific tool filtering", () => { + it("should apply global tool policy when no agent-specific policy exists", () => { + const cfg: ClawdbotConfig = { + agent: { + tools: { + allow: ["read", "write"], + deny: ["bash"], + }, + }, + routing: { + agents: { + main: { + workspace: "~/clawd", + }, + }, + }, + }; + + const tools = createClawdbotCodingTools({ + config: cfg, + sessionKey: "agent:main:main", + workspaceDir: "/tmp/test", + agentDir: "/tmp/agent", + }); + + const toolNames = tools.map((t) => t.name); + expect(toolNames).toContain("read"); + expect(toolNames).toContain("write"); + expect(toolNames).not.toContain("bash"); + }); + + it("should apply agent-specific tool policy", () => { + const cfg: ClawdbotConfig = { + agent: { + tools: { + allow: ["read", "write", "bash"], + deny: [], + }, + }, + routing: { + agents: { + restricted: { + workspace: "~/clawd-restricted", + tools: { + allow: ["read"], // Agent override: only read + deny: ["bash", "write", "edit"], + }, + }, + }, + }, + }; + + const tools = createClawdbotCodingTools({ + config: cfg, + sessionKey: "agent:restricted:main", + workspaceDir: "/tmp/test-restricted", + agentDir: "/tmp/agent-restricted", + }); + + const toolNames = tools.map((t) => t.name); + expect(toolNames).toContain("read"); + expect(toolNames).not.toContain("bash"); + expect(toolNames).not.toContain("write"); + expect(toolNames).not.toContain("edit"); + }); + + it("should allow different tool policies for different agents", () => { + const cfg: ClawdbotConfig = { + routing: { + agents: { + main: { + workspace: "~/clawd", + // No tools restriction - all tools available + }, + family: { + workspace: "~/clawd-family", + tools: { + allow: ["read"], + deny: ["bash", "write", "edit", "process"], + }, + }, + }, + }, + }; + + // main agent: all tools + const mainTools = createClawdbotCodingTools({ + config: cfg, + sessionKey: "agent:main:main", + workspaceDir: "/tmp/test-main", + agentDir: "/tmp/agent-main", + }); + const mainToolNames = mainTools.map((t) => t.name); + expect(mainToolNames).toContain("bash"); + expect(mainToolNames).toContain("write"); + expect(mainToolNames).toContain("edit"); + + // family agent: restricted + const familyTools = createClawdbotCodingTools({ + config: cfg, + sessionKey: "agent:family:whatsapp:group:123", + workspaceDir: "/tmp/test-family", + agentDir: "/tmp/agent-family", + }); + const familyToolNames = familyTools.map((t) => t.name); + expect(familyToolNames).toContain("read"); + expect(familyToolNames).not.toContain("bash"); + expect(familyToolNames).not.toContain("write"); + expect(familyToolNames).not.toContain("edit"); + }); + + it("should combine global and agent-specific deny lists", () => { + const cfg: ClawdbotConfig = { + agent: { + tools: { + deny: ["browser"], // Global deny + }, + }, + routing: { + agents: { + work: { + workspace: "~/clawd-work", + tools: { + deny: ["bash", "process"], // Agent deny + }, + }, + }, + }, + }; + + const tools = createClawdbotCodingTools({ + config: cfg, + sessionKey: "agent:work:slack:dm:user123", + workspaceDir: "/tmp/test-work", + agentDir: "/tmp/agent-work", + }); + + const toolNames = tools.map((t) => t.name); + // Both global and agent denies should be applied + expect(toolNames).not.toContain("browser"); + expect(toolNames).not.toContain("bash"); + expect(toolNames).not.toContain("process"); + }); + + it("should work with sandbox tools filtering", () => { + const cfg: ClawdbotConfig = { + agent: { + sandbox: { + mode: "all", + scope: "agent", + tools: { + allow: ["read", "write", "bash"], // Sandbox allows these + deny: [], + }, + }, + }, + routing: { + agents: { + restricted: { + workspace: "~/clawd-restricted", + sandbox: { + mode: "all", + scope: "agent", + }, + tools: { + allow: ["read"], // Agent further restricts to only read + deny: ["bash", "write"], + }, + }, + }, + }, + }; + + const tools = createClawdbotCodingTools({ + config: cfg, + sessionKey: "agent:restricted:main", + workspaceDir: "/tmp/test-restricted", + agentDir: "/tmp/agent-restricted", + sandbox: { + enabled: true, + sessionKey: "agent:restricted:main", + workspaceDir: "/tmp/sandbox", + agentWorkspaceDir: "/tmp/test-restricted", + workspaceAccess: "none", + containerName: "test-container", + containerWorkdir: "/workspace", + docker: {} as any, + tools: { + allow: ["read", "write", "bash"], + deny: [], + }, + }, + }); + + const toolNames = tools.map((t) => t.name); + // Agent policy should be applied first, then sandbox + // Agent allows only "read", sandbox allows ["read", "write", "bash"] + // Result: only "read" (most restrictive wins) + expect(toolNames).toContain("read"); + expect(toolNames).not.toContain("bash"); + expect(toolNames).not.toContain("write"); + }); +});