Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/tools/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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"],
Expand Down Expand Up @@ -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;
}
Expand Down
13 changes: 7 additions & 6 deletions tests/unit/tool_index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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"));
Expand All @@ -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");
});

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