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
Original file line number Diff line number Diff line change
Expand Up @@ -345,3 +345,50 @@ describe("canUseTool MCP approval enforcement", () => {
);
});
});

describe("canUseTool auto mode hands-off approval", () => {
beforeEach(() => {
clearMcpToolMetadataCache();
});

// Auto mode advertises hands-off approval; it must not prompt for the write
// and shell tools that drive every real task.
it.each(["Bash", "BashOutput", "KillShell", "Edit", "Write", "NotebookEdit"])(
"auto-allows %s in auto mode without prompting",
async (toolName) => {
const context = createContext(toolName, {
session: {
permissionMode: "auto",
settingsManager: {
getRepoRoot: vi.fn().mockReturnValue("/repo"),
},
},
});

const result = await canUseTool(context);

expect(result.behavior).toBe("allow");
expect(context.client.requestPermission).not.toHaveBeenCalled();
},
);

// Guard against the fix leaking into default mode, where these tools should
// still go through the manual permission prompt.
it.each(["Bash", "Edit", "Write"])(
"still prompts for %s in default mode",
async (toolName) => {
const context = createContext(toolName, {
session: {
permissionMode: "default",
settingsManager: {
getRepoRoot: vi.fn().mockReturnValue("/repo"),
},
},
});

await canUseTool(context);

expect(context.client.requestPermission).toHaveBeenCalled();
},
);
});
8 changes: 7 additions & 1 deletion packages/agent/src/adapters/claude/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,13 @@ const BASE_ALLOWED_TOOLS = [
];

const AUTO_ALLOWED_TOOLS: Record<string, Set<string>> = {
auto: new Set(BASE_ALLOWED_TOOLS),
// Auto mode is hands-off: it auto-approves file edits and shell commands on
// top of the base read/search/web/agent tools. Without WRITE_TOOLS and
// BASH_TOOLS here, Edit/Write/Bash fall through to a manual prompt on every
// call, which contradicts what the mode advertises. MCP tools are still gated
// separately (do_not_use is denied, needs_approval still prompts) in
// canUseTool, so auto stays narrower than bypassPermissions.
auto: new Set([...BASE_ALLOWED_TOOLS, ...WRITE_TOOLS, ...BASH_TOOLS]),
default: new Set(BASE_ALLOWED_TOOLS),
acceptEdits: new Set([...BASE_ALLOWED_TOOLS, ...WRITE_TOOLS]),
plan: new Set(BASE_ALLOWED_TOOLS),
Expand Down
2 changes: 1 addition & 1 deletion packages/agent/src/execution-mode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ if (ALLOW_BYPASS) {
{
id: "auto",
name: "Auto Mode",
description: "Use a model classifier to approve/deny permission prompts",
description: "Auto-approve file edits and shell commands",
},
);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/sessions/executionModes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const availableModes: ModeInfo[] = [
{
id: "auto",
name: "Auto Mode",
description: "Use a model classifier to approve/deny permission prompts",
description: "Auto-approve file edits and shell commands",
},
];

Expand Down
Loading