diff --git a/src/tools/index.js b/src/tools/index.js index 46b74eb8..de0c281f 100644 --- a/src/tools/index.js +++ b/src/tools/index.js @@ -18,7 +18,7 @@ import { webSearch, webExtract } from "./web.js"; /** * Maps tool names to required permission scopes. * A tool registers only when ALL its required permissions are in the enabled set. - * Clarify and execute_code are exempt (always registered) since they require zero permissions. + * Clarify, execute_code, sampling, and shell are exempt (always registered). */ export const TOOL_PERMISSIONS = { clarify: ["filesystem:read", "filesystem:write"], @@ -260,7 +260,8 @@ export async function buildToolConfig(options) { switch (toolName) { case "clarify": case "executeCode": - case "sampling": { + case "sampling": + case "shell": { tools.push(TOOLS[toolName]); continue; } diff --git a/tests/unit/tool_index.test.js b/tests/unit/tool_index.test.js index 3bbf0439..fda72324 100644 --- a/tests/unit/tool_index.test.js +++ b/tests/unit/tool_index.test.js @@ -81,15 +81,16 @@ describe("tools - buildToolConfig", () => { delete process.env.CUSTOM_SEARCH_URL; }); - it("returns clarify + executeCode + sampling + date + scanAgents with filesystem:read", async () => { + it("returns clarify + executeCode + sampling + shell + date + scanAgents with filesystem:read", async () => { const { buildToolConfig } = await import("../../src/tools/index.js"); const tools = await buildToolConfig({ permissions: ["filesystem:read"], maxReadSize: "1mb" }); const toolNames = tools.map((t) => t.name); - // filesystem:read enables: clarify, executeCode, sampling (always), compactContext, scanAgents, + // filesystem:read enables: clarify, executeCode, sampling, shell (always), compactContext, scanAgents, // sessionSearch, skillView, skillsList, date assert.ok(toolNames.includes("clarify")); assert.ok(toolNames.includes("executeCode")); assert.ok(toolNames.includes("sampling")); + assert.ok(toolNames.includes("shell")); assert.ok(toolNames.includes("date")); assert.ok(toolNames.includes("scanAgents")); assert.ok(toolNames.includes("sessionSearch")); @@ -116,8 +117,8 @@ describe("tools - buildToolConfig", () => { "sessionSearch should register with filesystem:read", ); assert.ok(toolNames.includes("sampling"), "sampling should register (no perms needed)"); - // shell requires process:spawn which is not enabled - assert.ok(!toolNames.includes("shell"), "shell should NOT register without process:spawn"); + // shell is exempt from permission gating (always registered) + assert.ok(toolNames.includes("shell"), "shell should register (exempt)"); assert.ok(!toolNames.includes("process"), "process should NOT register without process:spawn"); }); @@ -164,9 +165,9 @@ describe("tools - buildToolConfig", () => { maxReadSize: "2mb", }); const toolNames = tools.map((t) => t.name); - // filesystem:read enables: clarify, executeCode, sampling (always), compactContext, scanAgents, + // filesystem:read enables: clarify, executeCode, sampling, shell (always), compactContext, scanAgents, // sessionSearch, skillView, skillsList, date - assert.strictEqual(toolNames.length, 9); + assert.strictEqual(toolNames.length, 10); assert.ok(toolNames.includes("clarify")); assert.ok(toolNames.includes("executeCode")); assert.ok(toolNames.includes("sampling"));