Claude Code 源码解析 Part 2:权限系统、Swarm 审批与 Sandbox Runtime

Claude Code 源码解析 Part 2:权限系统不是“弹窗”,而是多 Agent Runtime 的控制面

上一篇我拆了 Claude Code 的多 Agent 编排,核心结论是:

Claude Code 的多 Agent 不是几个 prompt 互相调用,而是 Coordinator + Task Runtime + Mailbox + Backend 组成的一套运行时。

上一篇链接:
Claude Code 多 Agent 编排总览

但如果只有编排,没有控制,这套系统很快就会失控。

这就引出 Claude Code 另一个更关键、也更容易被低估的模块:

权限系统。

很多人一提权限,会先想到“执行 Bash 之前弹一个确认框”。

这对单 agent CLI 来说还算够用;但对 Claude Code 这种支持:

  • 主 agent
  • background agent
  • in-process teammate
  • remote session
  • MCP server

的系统来说,权限早就不是 UI 细节,而是runtime 的控制面

这篇文章想讲清楚四件事:

  1. Claude Code 的权限决策链路到底怎么走
  2. 单 agent 和多 agent 的权限模型是怎么统一的
  3. 沙箱和权限不是一回事,它们分别解决什么问题
  4. 为什么这套设计对多 Agent 研究尤其有启发


一、Claude Code 的权限系统,核心不是“允许/拒绝”,而是把决策拆成多个阶段

如果从源码入口看,Claude Code 的权限不是散落在各个工具里临时判断,而是有一条比较清晰的控制链:

  • src/utils/permissions/permissions.ts
  • src/hooks/toolPermission/PermissionContext.ts
  • src/hooks/toolPermission/handlers/interactiveHandler.ts
  • src/hooks/toolPermission/handlers/coordinatorHandler.ts
  • src/hooks/toolPermission/handlers/swarmWorkerHandler.ts
  • src/tools/BashTool/bashPermissions.ts

这几层组合起来之后,Claude Code 的权限检查大致分成下面几步:

  1. 工具发起调用
  2. 静态规则判断
  3. Hook / classifier 等自动决策
  4. 如果仍然不确定,则进入 ask 路径
  5. 由不同 handler 决定 ask 应该怎么被处理

关键在于,它没有把权限简化成一个同步布尔值,而是把它建模成了一个 richer result:

  • allow
  • deny
  • ask

这看起来只是多一个状态,但其实影响非常大。

因为对于 agent runtime 来说,真正麻烦的从来不是“能不能执行”,而是:

当系统还不能安全地下结论时,应该把决策交给谁、通过什么通道交给谁、等待期间如何保持运行时一致性。

Claude Code 在这一点上是非常工程化的。


二、PermissionContext 是真正的中枢:它把规则、用户、classifier、hook 和日志都绑在一起

PermissionContext.ts 是这套系统最值得读的文件之一。

它做的事情,不只是“包装一下弹窗上下文”,而是把一次权限请求变成一个可以被 runtime 消化的对象。

它至少统一了这些能力:

  • 记录 tool、input、assistantMessage、toolUseID
  • 记录和上报 permission decision
  • 把用户允许动作转成 PermissionUpdate
  • 持久化“always allow / always deny”这类规则
  • 支持 hook 先行决策
  • 支持 bash classifier 自动批准
  • 支持 abort、reject、feedback、content block 回流

这意味着 Claude Code 的权限系统不是“UI 提示层”,而是一个有副作用编排能力的事务上下文

这点很重要。

因为权限通过之后,并不是简单返回一个 true,而是可能要同时完成:

  • 修改输入参数
  • 写入新的 permission rule
  • 更新 AppState 中的 permission context
  • 记录审计日志
  • 继续 tool execution

也就是说,Claude Code 把“批准一次工具调用”设计成了一个可追踪、可持久化、可回放的运行时事件。


三、真正的权限决策链路:先走确定性规则,再走自动化判断,最后才找人

很多工具会把用户确认放在最前面:不确定就弹窗。

Claude Code 更像一个真正的控制系统,它先尽量把决策自动化。

从源码抽象后,大致是这条链路:

第 1 层:显式规则

permissions.tspermissionSetup.ts 中,Claude Code 会先加载不同来源的规则:

  • policySettings
  • projectSettings
  • userSettings
  • localSettings
  • cliArg
  • session
  • command

这一步体现了很清晰的“规则来源层级”思维:

权限不是一份配置,而是多来源规则的合并结果。

这种设计的现实意义是:

  • 企业策略可以高优先级覆盖
  • 项目级规则可以表达仓库约束
  • 用户级规则可以表达个人习惯
  • session 级规则可以承载临时授权

很多 agent 产品做不到这一步,结果就是安全策略无法分层,只能“一刀切”。

第 2 层:工具级安全逻辑

不同工具还有自己的领域逻辑。

最典型的是 Bash。

bashPermissions.ts 不是简单用一个字符串前缀匹配,而是做了很多细粒度工作:

  • shell command parsing
  • subcommand prefix 提取
  • compound command 拆分
  • wildcard / prefix rule 匹配
  • 语义级安全检查
  • 高风险 wrapper / interpreter 检测

这说明 Claude Code 很明确地知道一件事:

Bash 权限不是“工具权限”,而是“代码执行权限”。

因此它不能只靠通用权限框架,还需要 Bash 自己的安全语义层。

第 3 层:Hook 与 classifier

如果规则层没有直接裁定,接下来 Claude Code 会尝试自动化决策:

  • hook
  • bash classifier
  • 某些模式下的 yolo / transcript classifier 逻辑

这里最值得注意的是:Claude Code 把 classifier 当作自动化裁判,但并没有把它当作最终主宰。

它更像是在做:

  • 规则优先
  • 自动化推断补位
  • 实在不确定才进入 ask

这是一种很成熟的工程姿态。

因为 classifier 再强,本质上也是概率系统;而权限系统要承担的是错误成本。


四、三种 ask handler,是 Claude Code 权限系统最精彩的地方

如果把 allow / deny / ask 看作协议,那真正决定系统气质的,是 ask 怎么处理。

Claude Code 并没有用一个统一弹窗解决所有情况,而是针对不同运行时准备了不同 handler。

1. interactiveHandler:主会话的交互式权限流

文件:

  • src/hooks/toolPermission/handlers/interactiveHandler.ts

这个 handler 是主 agent 的标准权限路径。

它做的事情比“弹框”复杂得多:

  • 把请求压入 confirm queue
  • 并行等待 hook / classifier / 用户交互 / bridge response / channel response
  • resolveOnce 避免多个异步源重复 resolve
  • 用户开始交互后会终止 classifier 自动批准窗口

这一点很有意思:Claude Code 不是把用户和自动化串行排队,而是在做竞争式权限决策

谁先得出可靠结果,谁赢。

从系统设计上说,这明显比“先等 classifier,classifier 不行再弹窗”更顺滑,也更节省时间。

2. coordinatorHandler:Coordinator worker 的半自动流

文件:

  • src/hooks/toolPermission/handlers/coordinatorHandler.ts

这里的逻辑更偏 orchestrator:

  • 先跑 hook
  • 再跑 classifier
  • 都没结果再回落到 interactive dialog

它的关键不是复杂度,而是角色差异。

Claude Code 明显在区分:

  • 主交互 agent
  • coordinator worker

也就是说,不同 agent 角色即便共用同一套权限框架,进入 ask 状态之后的处理优先级仍然可以不同。

这非常值得学。

3. swarmWorkerHandler:多 Agent 协作下的权限转发

文件:

  • src/hooks/toolPermission/handlers/swarmWorkerHandler.ts
  • src/utils/swarm/permissionSync.ts
  • src/utils/swarm/leaderPermissionBridge.ts

这是我觉得最有启发的一部分。

在 swarm 模式下,一个 worker 如果要写文件、跑 Bash,它不应该各自弹一个权限框给用户。

Claude Code 的做法是:

  • 如果 classifier 能自动批,就先批
  • 否则把 permission request 发给 leader
  • leader 统一决策
  • worker 等待 leader 的响应再继续执行

这说明在 Claude Code 里,权限已经不是“用户和当前 agent 的关系”,而是:

leader 与 worker 的协调协议。

这件事非常关键。

因为一旦进入多 Agent 时代,权限系统如果还停留在“每个 agent 自己弹窗”,就会很快崩溃:

  • 用户界面会爆炸
  • 审批来源会混乱
  • 责任链不清楚
  • 无法做统一审计

Claude Code 通过 leader bridge + mailbox sync 解决了这个问题。

我认为这是它比很多“多 agent 框架 demo”成熟得多的根本原因之一。


五、单 agent 和多 agent 的统一,不是通过共用弹窗,而是通过共用 PermissionDecision 协议

表面上看,主 agent 和 swarm worker 的权限处理方式差很多:

  • 一个走 UI queue
  • 一个走 leader mailbox

但从系统内部看,它们其实统一在同一个抽象上:

  • PermissionDecision
  • PermissionUpdate
  • PermissionContext

这意味着 Claude Code 并不是为每种场景重新发明权限系统,而是把差异控制在 handler 这一层。

这是一种非常好的分层:

统一的部分

  • 规则来源
  • allow / deny / ask 协议
  • 更新和持久化规则的方法
  • decision logging
  • tool use context

变化的部分

  • ask 交给谁
  • 等待谁回复
  • UI 怎么呈现
  • 是否存在 bridge 或 mailbox

这给我们一个很重要的启发:

多 Agent 不是多套权限系统,而是一套权限协议 + 多种决策承载通道。

这个思路非常值得抄。


六、Bash classifier 真正扮演的,不是“安全模型”,而是降低人类审批负担的中间层

很多人一听 classifier,就会往“安全 AI 审核”方向理解。

但从 Claude Code 的实现看,它更像一个 practical middle layer:

  • 能自动批准明显安全的 bash 行为
  • 减少用户被频繁打断
  • 对高风险命令保持 ask 或 deny

也就是说,classifier 在这里不是为了替代权限系统,而是为了让权限系统能在真实工作流中被接受

这是一个很成熟的产品判断。

因为如果没有 classifier,终端 agent 在写代码、跑测试、读日志时会频繁触发确认;

而如果完全依赖 classifier,又会把高风险错误放大。

Claude Code 选的是中间路线:

  • rule 是硬边界
  • classifier 是减压阀
  • human approval 是最终兜底

这比“全自动”或“全手动”都更现实。


七、沙箱不是权限系统的替代,而是权限系统的执行边界

这是本文最想强调的一点。

很多人容易把“权限”和“沙箱”混成一个概念。

Claude Code 明显把二者分开了。

相关文件:

  • src/utils/sandbox/sandbox-adapter.ts
  • src/tools/BashTool/shouldUseSandbox.ts
  • src/utils/permissions/filesystem.ts

它们大致承担的是两个不同问题:

权限系统回答的是:

  • 这次 tool use 应不应该被允许
  • 允许后要不要写入新的 permission rule
  • 由谁批准这次操作

沙箱回答的是:

  • 即使允许了,这次执行最多能碰到哪里
  • 文件系统写入边界是什么
  • 网络域名边界是什么
  • 是否忽略某些违规

Claude Code 的 sandbox-adapter.ts 很能说明问题。

它不是一个孤立安全模块,而是把 Claude Code 现有设置与 permission rule 转换成 @anthropic-ai/sandbox-runtime 可执行的 runtime config。

也就是说,沙箱不是另起炉灶的世界,而是:

把上层规则投影成 OS 级运行限制。

这个分层非常正确。

如果没有权限层,沙箱会过于僵硬,用户体验很差;

如果没有沙箱层,权限通过之后工具仍然可能做得太多。

Claude Code 的做法是:

  • 权限层负责“决策”
  • 沙箱层负责“边界”

这是控制系统中非常经典、也非常必要的一刀。


八、filesystem 权限值得单独研究:Claude Code 明显在防止“配置文件成为逃逸点”

src/utils/permissions/filesystem.ts 非常值得单独看。

这里面有一条非常明确的安全思想:

Claude Code 不只是防止你改错业务代码,它更在防止 agent 修改会影响自身权限和执行环境的配置文件。

例如它会特别保护:

  • .claude/settings.json
  • .claude/settings.local.json
  • .claude/skills
  • .gitconfig
  • .gitmodules
  • shell profile
  • .mcp.json

这背后的逻辑很清楚:

真正危险的不是“改错了一个业务文件”,而是 agent 改写了自己未来的能力边界。

这一点和很多简单的 coding agent 权限设计完全不同。

很多系统只在乎“能不能写项目目录”,Claude Code 已经在考虑“能不能通过改配置间接扩大未来权限”。

这说明它对长期运行 agent 的风险模型是比较成熟的。


九、MCP 审批说明了 Claude Code 的权限面不只在本地工具

还有一个容易被忽略的点:

  • src/services/mcpServerApproval.tsx

这说明 Claude Code 的权限边界不仅发生在 Bash / Edit / Write 这种本地工具上,也发生在外部能力接入这一层。

具体来说,项目级 MCP server 并不是连上就算完了,而是有 pending approval 流程。

这说明 Claude Code 把“给 agent 接入一个新外部系统”视为一种高阶权限行为。

这个判断非常重要。

因为在 agent 时代,风险不只来自本地 shell,还来自:

  • 新增可调用 API
  • 新增外部资源访问面
  • 新增远程执行能力

MCP 审批本质上是在控制 agent 的 capability surface。

这也是为什么我觉得 Claude Code 的权限系统不是工具层小模块,而是整个 runtime 的能力治理层。


十、对多 Agent 研究最有启发的三个点

最后,总结三条我觉得最值得研究者带走的结论。

1. 权限系统必须协议化

只要系统进入多 agent、异步执行、remote session,权限就不可能继续是“一个同步弹窗”。

它必须是协议:

  • 谁发起
  • 谁审批
  • 谁等待
  • 谁记录
  • 决策如何持久化

Claude Code 在这点上已经走到了真正工程系统的层级。

2. 多 Agent 的统一,不靠统一 UI,而靠统一 decision model

主 agent、coordinator、swarm worker 的 ask 处理方式都不同,但都归一到同一个 PermissionDecision 协议。

这是一种很强的架构能力。

3. 沙箱和权限一定要分层

权限决定“是否放行”,沙箱决定“放行后最多能做什么”。

如果把这两件事揉在一起,系统会要么不安全,要么不可用。

Claude Code 这套分工非常值得借鉴。


结语:Claude Code 的权限系统,本质上是在给 Agent Runtime 建立宪法

上一篇写多 Agent 编排时,我说 Claude Code 更像在做 Agent OS。

这篇读完之后,我会更进一步:

Claude Code 的权限系统,像是在给这套 Agent OS 写宪法。

它规定:

  • 哪些能力能默认放行
  • 哪些能力需要领导层审批
  • 哪些能力即使放行也必须被沙箱约束
  • 哪些配置文件永远不能随便碰
  • 多 Agent 之间如何转发权限请求

这已经不是普通 CLI 的思路了。

它说明真正成熟的 agent 系统竞争,最终一定会进入这些地方:

  • control plane
  • permission protocol
  • capability governance
  • sandbox boundary
  • auditability

如果你也在做多 Agent,这一套值得认真研究。

下一篇我准备继续拆:

QueryEngine 与主循环:Claude Code 为什么不是聊天机器人,而是一个 runtime。

深求社区(DeepSeek.club)出品,国内领先的AI大模型及应用开源社区!