9.3 权限配置层级

模型: claude-opus-4-6 (anthropic/claude-opus-4-6) 生成日期: 2026-02-17


OpenCode 的权限系统不是由一个单一的配置源决定的,而是由多个层级的规则集叠加而成。本节将解析这些层级的来源、加载顺序和覆盖关系。

9.3.1 Agent 默认权限

每个 Agent 在定义时都携带了一组默认的权限规则。在第 6 章中我们已经看到,build Agent(OpenCode 的主要 Agent)的默认权限配置如下:

// agent/agent.ts - build Agent 的权限
permission: {
  "*": "allow",          // 默认允许所有工具
  doom_loop: "ask",      // 循环检测时询问用户
  external_directory: {   // 外部目录访问
    "~/.ssh": "deny",    // 禁止访问 SSH 密钥
    "~/.gnupg": "deny",  // 禁止访问 GPG 密钥
    "*": "ask",           // 其他外部目录询问用户
  },
  read: {
    "*.env*": "ask",     // 读取 .env 文件时询问用户
    "*": "allow",         // 其他文件允许读取
  },
}

而其他辅助 Agent(如 exploresummarytitle)通常有更严格的默认权限——它们只能使用少数只读工具。

这些默认权限通过 fromConfig() 转换为 Ruleset:

9.3.2 用户配置权限

用户可以通过 opencode.json 中的 agent 字段覆盖 Agent 的权限配置:

在 Agent 加载时,用户配置的权限会与 Agent 默认权限合并(详见第 6 章 Agent 加载逻辑)。关键代码在 config.tsloadAgent() 中:

由于 merge 是简单的数组拼接,用户配置的规则会排在默认规则之后,从而在 findLast() 评估时获得更高的优先级。

9.3.3 Session 运行时授权

除了静态配置之外,Session 在创建时还可以携带额外的运行时权限规则。这些规则通常来自用户在 Session 创建界面上的选择,或者由 Plugin 注入:

在权限评估时,Session 的权限作为最后一层规则集参与合并:

Session 权限作为最后加入的规则集,拥有最高的覆盖优先级。

完整的优先级链

一个具体的例子:

9.3.4 always 选项——永久授权记忆

当用户面对 ask 类型的权限请求时,有三种回复选项:

回复
含义

once

仅此一次允许

always

始终允许(在当前项目范围内)

reject

拒绝

当用户选择 always 时,系统会将新的 allow 规则添加到运行时的已批准列表中:

这段代码有两个关键行为:

  1. 持久化授权:将 always 对应的模式作为新的 allow 规则添加到 s.approved 列表中。后续相同的权限请求会直接匹配这条规则而无需再次询问。

  2. 批量审批:当用户选择 always 后,系统会自动检查同一 Session 中是否有其他等待中的权限请求也被这个新规则覆盖——如果是,自动审批它们。这避免了用户需要逐一审批多个类似请求的烦恼。

已批准规则的作用域

已批准的规则存储在 Instance.state() 中,这意味着它们的生命周期与项目实例绑定:

值得注意的是,源码中有一个 TODO 注释:

这意味着当前版本中,always 授权只在 OpenCode 的单次运行期间有效——重启后会丢失。这是一个有意的设计选择:在没有提供管理界面让用户查看和撤销已授权规则之前,不持久化这些规则更加安全。


本节小结

OpenCode 的权限配置形成三层优先级链:Agent 默认权限(最低)→ 用户配置权限 → Session 运行时权限(最高)。所有层级通过数组拼接合并,利用 findLast() 自然实现后来者优先的覆盖语义。always 回复会在运行时动态添加 allow 规则,并自动批量审批同 Session 中相同类型的等待请求。当前版本的 always 授权不持久化到磁盘,重启后失效——这是在缺少管理 UI 时的安全选择。

Last updated