权限与审批
权限系统回答一个问题:这个工具调用能不能执行、还是要先问人。它由三层组成——模式(用户当前选择)、项目规则(WorkApprovalRules)、工具风险(读 / 写)。规则永远优先于模式。
五档权限模式
| 模式 | 行为 | 典型场景 |
|---|---|---|
plan 计划 | 非只读工具直接拒绝(不是「问了再执行」),返回固定拒绝话术 | 只想让模型出方案,不许动文件 |
readonly 只读 | 只读工具放行,写工具直接拒绝 | 读代码、查资料、做分析 |
ask 询问(默认) | 写操作逐个请求批准 | 第一次在陌生仓库里动手 |
auto 自动 | 常规写操作直接执行,敏感操作仍会询问 | 信任的重复性任务 |
bypass 绕过 | 全部放行,包括规则里被 deny 之外的一切 | 一次性、隔离环境 |
非法值一律回落到 ask(同时兼容历史写法 read-only / read_only)。
判定顺序
- 模式硬约束:
plan/readonly先判,命中即结束——这两个模式不受项目规则影响。 - 项目规则:按工具名与路径匹配
WorkApprovalRule;deny覆盖任何模式,allow也会放行ask/auto下的调用。 - 模式 + 工具风险:没有命中规则时,只读工具按模式放行,写工具按模式决定「直接执行」还是「请求批准」。
项目规则模型
| 字段 | 说明 |
|---|---|
ToolName | 工具名,支持 * 通配 |
PathPattern | 可选路径匹配;只对文件类工具生效(读写/删除/移动/打补丁五种),其他工具不参与路径匹配 |
Decision | allow 或 deny,其他取值创建失败 |
IsEnabled | 规则可临时停用而不删除 |
匹配评分:精确工具名优先于通配,带路径的规则优先于不带路径;同一工具 + 同一路径模式的重复规则会在创建时合并成一条,避免规则抖动。
运行模式对权限的影响
interactive:保持所选模式,逐步确认。autopilot:把ask提升为auto(少打断),但plan/readonly不会被提升——强约束不被托管执行绕过。
三条入口的差异
| 入口 | 权限来源 |
|---|---|
| 编码会话 | 完整三层:模式 + 项目规则 + 工具风险;会话上持久化 PermissionMode 与 AgentMode |
| 对话页 | 模式 + 逐工具覆盖(ToolApprovalOverrides);不读取项目规则——对话没有项目上下文 |
| 工作流节点 | 节点配置的 toolApprovals 与运行级 toolApprovalMode 叠加,后者只覆盖未显式配置的工具 |
审批交互链路
- 判定器返回「需要批准」后,Agent 循环发出
approval_request事件(含工具名与参数)。 - 前端渲染成审批卡片:批准 / 拒绝,可附带理由。
- 批准则执行该次调用并把结果回填;拒绝则把拒绝原因作为工具结果返回给模型,让它换方案。
- 与
question事件的区别:approval_request是「要不要执行这个工具」,question是「向用户追问信息」,两者都有自己的 id,不能混用。
安全建议
- 在陌生仓库先跑
ask,确认工具面符合预期后再切auto。 - 把「永不希望自动执行」的工具(例如删除文件、推送代码)写成
deny规则,这样连auto/bypass都不会绕过。 - 用路径规则限定作用范围:例如允许写
docs/**,其余仍走确认。 - MCP 工具与内置工具共用这套判定;接新服务器先用
ask跑一遍,确认它到底会调用什么。
观测与排查
- 规则接口返回
WorkApprovalRuleDto;非法 decision 报approvalRule.invalidDecision,规则不存在报approvalRule.notFound。 - 被拒绝的工具调用会在消息流里显示为工具结果(含拒绝原因),便于解释「为什么模型没做那件事」。
- 怀疑权限覆盖顺序时,先确认是否命中项目规则(规则优先于模式),再确认运行模式是否把
ask提升成了auto。