From e2cc10fb634f0950bade18524cbc88d832c60f11 Mon Sep 17 00:00:00 2001 From: Jason Mulligan Date: Sun, 2 Aug 2026 12:21:10 -0400 Subject: [PATCH] fix: exempt shell tool from permission gating in buildToolConfig The shell tool is essential for agents that need command execution (coding, debug, testing, etc.) and the sandbox permissions are already enabled in config. Adding it to the exempt case alongside clarify, executeCode, and sampling ensures it's always registered. --- src/tools/index.js | 5 +++-- tests/unit/tool_index.test.js | 13 +++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) 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"));