Skip to content
源码分析手册

Auto Mode:基于智能分类与沙箱信任的自动化审批

Auto Mode(自动模式)是 Claude Code 权限架构中最体现设计智慧的部分。它并不是简单地关闭权限检查,而是引入了一个基于小模型的“分类器流水线”和“物理沙箱信任机制”,来替人类进行初步的安全合规性裁决。

核心概念

Auto Mode 本质上是 审批权的动态代理(Delegated Decision Making)

它旨在解决开发者面临的“审批疲劳”问题:如果每一个只读操作(如读文件、列出目录)都要用户点击确认,那么开发效率将大打折扣。

  • 它不是什么:它不是“全能绿灯”。它依然拥有一套严密的拦截准则,针对高风险、不可逆的操作(如跨目录修改、敏感网络连接、特权命令),它会自动降级回人工审批。
  • 它的核心理念:将人类的时间花在关键决策上,将琐碎的合规性判断交给分类器(YOLO)和受物理隔离保护的沙箱。

代码里的真实逻辑

Auto Mode 的决策链由 claude-code-opensource/src/utils/permissions/classifierDecision.ts 核心驱动,遵循一个“三级阶梯式”的审批逻辑:

1. 静态白名单(Static Allowlist)

为了极致的响应速度,系统硬编码了一份 SAFE_YOLO_ALLOWLISTED_TOOLS。所有只读类工具(FileReadGrepGlob)、任务状态管理工具(TaskCreateTaskList)以及 UI 辅助工具都会命中此名单。这些操作被认为是绝对安全的,直接跳过任何判断逻辑,无感放行。

2. 分类器裁决(The YOLO Classifier)

对于超出白名单但又属于“常规开发”范畴的操作,系统会启动 yoloClassifier.ts(也被称为 YOLO 模型)。这是一个轻量化的分类器,它会迅速扫描模型当前的上下文:

  • 输入流:即将执行的命令(如 npm run build)、当前任务的目标、以及最近的操作历史。
  • 判定流:它会基于预设的安全策略给出三色裁决:Approve(自动执行)、Deny(告知模型被拒绝)或 Ask(升级给人类用户)。
  • 熔断机制:通过 denialTracking.ts 监控拒绝次数。如果模型在 Auto 模式下短时间内连续触发拒绝,系统会强行中断自动化流程,防止模型陷入无效重试的死循环。

3. 沙箱信任锚点(Sandbox Auto-Allow)

这是 Auto Mode 逻辑的深度补充。在执行 Bash 命令时,系统会调用 checkSandboxAutoAllow

  • 逻辑转换:如果当前环境已经开启了物理沙箱(OS-level isolation),且用户配置了 autoAllowBashIfSandboxed,系统会认为:“既然这个命令已经在被隔离的沙箱中运行,无法触碰核心系统,那么我们可以自动放行”。
  • 显式优先原则:即使处于沙箱环境,如果用户之前通过 /permissions 手动设置了针对该命令的 DenyAsk 规则,沙箱自动放行逻辑会立即失效。用户的显式意愿永远高于系统的自动猜测

别踩这些坑

  • 路径隔离(CWD Constraint):大部分自动放行仅限于当前 CWD(工作目录)。如果模型尝试修改用户目录之外的文件,即使在 Auto Mode 下也会触发显式的权限询问。
  • 风险分级sudo 命令、涉及 rm -rf 的全局删除、未经声明的网络请求(如向未知域名发送数据)属于高风险分级,分类器几乎总是会将其路由到 Ask 状态。
  • 身份敏感性:源码显示,Auto Mode 的某些高级逻辑(如基于 Transcript 的精细分类)带有 ant 用户标签检测。这意味着不同版本的 Claude Code(如企业版 vs 个人版)在自动化激进度上可能存在微调。
  • 沙箱完整性依赖:Sandbox Auto-Allow 的安全性完全依赖于操作系统的沙箱实现。如果沙箱本身配置有误或由于系统限制未生效,该功能会自动关闭以确保安全。

相关主题

源码锚点

📄 src/utils/permissions/classifierDecision.ts — `SAFE_YOLO_ALLOWLISTED_TOOLS`:内置的免审白名单。L56-85 of 99
typescript
const SAFE_YOLO_ALLOWLISTED_TOOLS = new Set([
  // Read-only file operations
  FILE_READ_TOOL_NAME,
  // Search / read-only
  GREP_TOOL_NAME,
  GLOB_TOOL_NAME,
  LSP_TOOL_NAME,
  TOOL_SEARCH_TOOL_NAME,
  LIST_MCP_RESOURCES_TOOL_NAME,
  'ReadMcpResourceTool', // no exported constant
  // Task management (metadata only)
  TODO_WRITE_TOOL_NAME,
  TASK_CREATE_TOOL_NAME,
  TASK_GET_TOOL_NAME,
  TASK_UPDATE_TOOL_NAME,
  TASK_LIST_TOOL_NAME,
  TASK_STOP_TOOL_NAME,
  TASK_OUTPUT_TOOL_NAME,
  // Plan mode / UI
  ASK_USER_QUESTION_TOOL_NAME,
  ENTER_PLAN_MODE_TOOL_NAME,
  EXIT_PLAN_MODE_TOOL_NAME,
  // Swarm coordination (internal mailbox/team state only — teammates have
  // their own permission checks, so no actual security bypass).
  TEAM_CREATE_TOOL_NAME,
  // Agent cleanup
  TEAM_DELETE_TOOL_NAME,
  SEND_MESSAGE_TOOL_NAME,
  // Workflow orchestration — subagents go through canUseTool individually
  ...(WORKFLOW_TOOL_NAME ? [WORKFLOW_TOOL_NAME] : []),
📄 src/utils/permissions/yoloClassifier.ts — 自动化分类器的核心 Prompt 与逻辑。L37-40 of 1496
typescript
  getBashPromptAllowDescriptions,
  getBashPromptDenyDescriptions,
} from './bashClassifier.js'
import {
📄 src/tools/BashTool/bashPermissions.ts — `checkSandboxAutoAllow`:基于沙箱环境的自动放行算法。L1270-1299 of 2622
typescript
function checkSandboxAutoAllow(
  input: z.infer<typeof BashTool.inputSchema>,
  toolPermissionContext: ToolPermissionContext,
): PermissionResult {
  const command = input.command.trim()

  // Check for explicit deny/ask rules on the full command (exact + prefix)
  const { matchingDenyRules, matchingAskRules } = matchingRulesForInput(
    input,
    toolPermissionContext,
    'prefix',
  )

  // Return immediately if there's an explicit deny rule on the full command
  if (matchingDenyRules[0] !== undefined) {
    return {
      behavior: 'deny',
      message: `Permission to use ${BashTool.name} with command ${command} has been denied.`,
      decisionReason: {
        type: 'rule',
        rule: matchingDenyRules[0],
      },
    }
  }

  // SECURITY: For compound commands, check each subcommand against deny/ask
  // rules. Prefix rules like Bash(rm:*) won't match the full compound command
  // (e.g., "echo hello && rm -rf /" doesn't start with "rm"), so we must
  // check each subcommand individually.
  // IMPORTANT: Subcommand deny checks must run BEFORE full-command ask returns.
📄 src/utils/permissions/denialTracking.ts — 防止自动化陷入死循环的熔断机制。L7-10 of 46
typescript
export type DenialTrackingState = {
  consecutiveDenials: number
  totalDenials: number
}

基于 Claude Code v2.1.88 开源快照的深度分析