权限状态机与渐进式授权从用户体验到子 Agent 代理《Claude Code 架构解密》精读笔记 · 第08篇覆盖章节第5章后半5.8-5.14| 页码p.127-140导语前两篇我们拆解了权限系统的骨架14步决策树和智能层YOLO Classifier 断路器。但一个安全系统是否真正可用最终取决于运行时的状态流转和用户的实际体验。权限不只是允许/拒绝的二元判断。它是一种动态的信任关系——用户对 Agent 的信任会随着使用逐步建立Agent 的自主权限也应该随之扩大。同时当多个 Agent 协作时权限如何在父子之间传播当模式切换时如何防止竞态条件这些问题远比写规则复杂。本篇是第5章权限深潜的收官之作我们将深入六种权限模式——从 default 到 dontAsk 的完整状态机渐进式信任 UX——如何在安全性和流畅性之间找到平衡子 Agent 的权限代理——多 Agent 场景下的权限传播8 个可复用设计模式——从判别联合到渐进式信任一、权限模式状态机1.1 六种权限模式Claude Code 定义了六种权限模式每种对默认行为有不同处理策略模式默认行为适用场景default每个操作都需确认新用户、敏感环境acceptEdits工作目录内的文件编辑自动允许日常开发最常用plan先制定计划再执行复杂多步任务autoAI 分类器自动判断高效开发模式bypassPermissions全部自动允许免疫检查仍生效完全信任环境dontAsk询问转为拒绝不弹提示后台任务、CI/CD六种模式不是简单的枚举——它们是一个有结构的状态机ShiftTab ShiftTab default ────────→ acceptEdits ────────→ bypassPermissions │ │ │ /plan │ └──→ plan 模式 ←──────────────────────────────┘ 保存 prePlanMode 之前的模式 退出时恢复到 prePlanMode auto 模式与 acceptEdits/bypass 互相切换 进入: stripDangerousPermissions() 退出: restoreDangerousPermissions() 门控: 断路器 设置 模型检查几个关键设计plan 模式是保存点模式进入 plan 时记住之前的模式退出时恢复。用户不会因为先想再干而丢失之前的信任设置。auto 模式有门控不是任何用户都能进入 auto——需要通过verifyAutoModeGateAccess()检查权限。bypassPermissions 不是无权限免疫检查如危险路径保护仍然生效即使在这个模式下也无法修改.bashrc或.claude/settings.json。1.2 模式切换的副作用处理模式切换不是简单地改变一个枚举值——它涉及复杂的副作用。permissionSetup.ts中的transitionPermissionMode函数处理了这些副作用进入 Auto 模式异步调用verifyAutoModeGateAccess()检查用户是否有权使用 Auto 模式调用stripDangerousPermissionsForAutoMode()临时剥离危险规则调用setAutoModeActive(true)激活分类器退出 Auto 模式调用restoreDangerousPermissions()恢复被剥离的规则调用setAutoModeActive(false)停用分类器设置退出标记setNeedsAutoModeExitAttachment()进入 Plan 模式保存当前模式到prePlanMode如果配置了useAutoModeDuringPlan保持分类器激活退出 Plan 模式恢复到prePlanMode保存的模式清除prePlanMode1.3 变换函数防竞态Auto 模式的门控检查verifyAutoModeGateAccess是一个异步操作——它需要联网验证用户的访问权限。这里存在一个微妙的竞态条件时间 T0用户按 ShiftTab 进入 Auto 模式 → 开始异步门控检查 时间 T1用户又按 ShiftTab切换回 default 模式 时间 T2门控检查完成返回结果 → 此时应该应用到哪个模式如果verifyAutoModeGateAccess直接返回修改后的上下文快照T2 时刻应用的将是基于 T0 状态的快照——但用户在 T1 已经切换了模式快照已经过时了。Claude Code 的解决方案是让门控检查返回一个变换函数Transform Function而非预计算的结果// 概念示意consttransformawaitverifyAutoModeGateAccess()// transform 是一个函数(currentContext) newContext// 应用时基于最新的 context而非 T0 时刻的快照updateContext(prevContexttransform(prevContext))这是函数式编程中延迟计算思想在并发场景的实用应用——将做什么和基于什么状态做分离。React 的setState(prev next)也采用了同样的模式。核心洞察当异步操作的结果需要应用到可变状态时返回变换函数而非快照是防止竞态的通用模式。1.4 危险权限的剥离与恢复Auto 模式有一个核心安全问题如果用户在设置中配置了Bash(python:*)这样的宽泛 allow 规则它会在分类器检查之前就允许执行python -c import os; os.system(rm -rf /)——因为 allow 规则匹配在分类器调用之前。解决方案是在进入 Auto 模式时扫描并剥离所有被认定为危险的 allow 规则进入 Auto 模式 1. 扫描所有 allow 规则 2. isDangerousBashPermission() 检测危险模式 3. 从上下文中剥离危险规则 → 保存到 strippedDangerousRules 4. 通知用户以下规则在 Auto 模式中被暂时禁用 退出 Auto 模式 1. 从 strippedDangerousRules 恢复规则 2. 重新应用到上下文危险模式的定义dangerousPatterns.ts包括30 种代码执行入口python、node、bash、eval、exec、sudo 等5 种匹配变体精确匹配、前缀语法、通配符后缀、空格后缀、选项通配符内部扩展的敏感工具curl、wget、git、kubectl、aws 等设计哲学系统不是禁止用户配置宽泛规则——在非 Auto 模式下这些规则正常生效。只是在 Auto 模式这个特定场景下因为分类器替代了人工判断才需要额外的安全措施。剥离-恢复而非删除尊重了用户的原始配置意图。二、渐进式信任 UX2.1 设计哲学按需打断、记住偏好、透明可控权限系统的后端再精巧如果 UX 设计不好用户要么一路狂点 Yes失去安全意义要么受够了弹框关掉 Agent失去产品价值。Claude Code 的权限 UX 围绕三个原则设计按需打断——只在真正需要用户决策时才弹出权限对话框记住偏好——提供下次不再询问机制减少重复打断透明可控——解释为什么需要权限允许事后管理规则2.2 三层组件架构权限 UI 采用三层架构将关注点清晰分离PermissionRequest路由层 根据 tool 类型分发到对应权限组件 PermissionDialog容器层 统一的对话框外壳圆角顶边框 标题 内容区 PermissionPrompt交互层 Select 组件 反馈输入 快捷键绑定路由层通过permissionComponentForTool()实现一个工具一个 UI策略工具权限组件特殊展示FileEditToolFileEditPermissionRequestdiff 视图BashToolBashPermissionRequest命令高亮 前缀规则建议FileWriteToolFileWritePermissionRequest新文件内容预览Glob/GrepFilesystemPermissionRequest共用组件WebFetchToolWebFetchPermissionRequestURL 展示ExitPlanModeV2ExitPlanModePermissionRequest复选选项AskUserQuestionAskUserQuestionPermissionRequest问卷式交互其他未知工具FallbackPermissionRequest通用回退为什么不用一个通用的确认框处理所有工具因为不同工具的风险谱和用户关注点完全不同Bash 命令的风险跨度最大从ls到rm -rf需要最精细的 UI包括命令高亮、前缀规则建议、破坏性命令警告文件编辑的用户关注点是改了什么最好的展示方式是 diff网络请求的用户关注点是访问了哪个 URL通用确认框会丢失这些上下文信息迫使用户在缺乏信息的情况下做决策——这正是权限疲劳Permission Fatigue的根源。2.3 Bash 权限的渐进式信任Bash 权限对话框是整个 UX 中最精心设计的部分体现了渐进式信任的核心思想Bash npm run build Yes ← 一次性允许 Yes, and dont ask again for [npm run:*] ← 可编辑的前缀规则 No ← 拒绝四个选项对应四种信任级别选项持久性信任级别场景Yes仅本次最低不确定的命令Yes, apply suggestions按后端建议中等后端已分析的安全命令Yes, don’t ask again for [prefix]持久化到设置较高npm run:*、git:*等模式Auto-approved分类器会话级最高AI 分类器判定为安全可编辑前缀规则是一个极为精巧的设计。当系统检测到命令符合主命令子命令模式时如npm run build会预填充一个可编辑的通配符规则npm run:*。用户可以保留默认npm run:*→ 允许所有 npm run 子命令缩小范围npm run build→ 只允许这个特定命令扩大范围npm:*→ 允许所有 npm 命令这种设计让用户主动参与安全决策而非被动接受系统的二选一。前缀提取算法getSimpleCommandPrefix有一个安全防护BARE_SHELL_PREFIXES集合阻止生成过于宽泛的规则。例如当命令是bash -c rm -rf /时不会生成bash:*这样的规则——因为bash:*允许执行任何 shell 脚本风险不可控。2.4 Diff 驱动的文件权限决策文件编辑权限不展示原始的编辑指令而是展示变更的 diffEdit src/config.ts - const timeout 5000 const timeout 10000 Yes Yes, allow all edits during this session No类似地当 Bash 命令是sed -i s/old/new/ file格式时系统不展示 sed 命令本身而是路由到SedEditPermissionRequest以 diff 形式展示变更。UX 原则展示用户关心的信息而非技术细节。用户关心的是文件会怎么改而非 sed 命令的正则语法。当用户在 VS Code/Cursor 等 IDE 中使用 Claude Code 时文件编辑权限请求会通过 RPC 自动在 IDE 中打开 diff 视图。用户可以在最熟悉的编辑器环境中审阅变更甚至修改 Claude 的编辑后再接受——这让用户从**“审批者升级为协作者”**。2.5 Tab 键反馈渐进式披露的典范权限对话框中有一个隐藏的高级功能——Tab 键展开文本输入框 Yes ← 聚焦时显示 Tab to amend tell Claude what to ← 按 Tab 后展开输入框 do next No tell Claude what to ← 拒绝时的反馈 do differently两个细节上下文敏感的占位符文案——接受时提示tell Claude what to do next引导下一步拒绝时提示tell Claude what to do differently引导替代方案。微妙的措辞差异引导用户提供不同类型的反馈。空提交取消——在输入模式下空提交会退出输入模式而非提交空反馈。防止了意外提交。这是**渐进式披露Progressive Disclosure**在 CLI 环境中的优秀实践默认展示简洁的选项高级功能通过发现性较弱但不碍事的方式提供。2.6 AI 解释与三层透明度权限对话框提供了三层透明度机制① CtrlE——AI 操作解释用户按 CtrlE 后系统调用 Haiku 模型小型快速模型来生成操作的风险评估- Explanation ───────────────────────── | Med risk | | Removes all installed packages and | | reinstalls them from scratch. | | Reasoning: I need to resolve | | dependency conflicts that cant be | | fixed incrementally. | | Risk: Temporary loss of dependencies | ────────────────────────────────────────三级风险着色LOW、MEDIUM、HIGH让用户无需阅读详细文字就能快速判断风险程度。这个功能是懒加载的——仅在用户按 CtrlE 时才调用 AI避免每次权限请求都消耗 token。加载时展示 shimmer 动画字符逐个高亮的流动效果视觉上暗示AI 正在思考。② 规则溯源展示每个权限对话框解释为什么需要确认Matched ask rule: Bash(rm:*) (/permissions to update rules)不仅告诉用户需要确认还告诉为什么需要确认和如何修改这个行为。配置提示/permissions直接指向修改入口。③ CtrlD——调试信息高级用户按 CtrlD 可以看到完整的权限决策路径用于排查复杂的权限问题。2.7 分类器自动审批的 UX 反馈当启用 Auto 模式时AI 分类器在后台异步运行。UI 层面的处理等待中标题区域显示 shimmer 动画文字Attempting to auto-approve…以 20fps 闪烁自动通过显示勾选标记命令自动执行用户覆盖即使分类器自动通过用户仍可手动操作shimmer 动画被提取到独立的classifierCheckingSubtitle组件避免 20fps 的时钟刷新导致整个对话框重渲染——这是性能感知影响 UX 的典型例子详见第11章终端渲染引擎的性能优化策略。2.8 离席通知当权限对话框等待用户响应超过 6 秒且无用户交互时系统发送桌面通知constDEFAULT_INTERACTION_THRESHOLD_MS6000;useNotifyAfterTimeout(notificationMessage,permission_prompt);通知消息根据工具类型定制Plan Mode 提示Claude Code needs your approval for the plan其他工具提示Claude needs your permission to use {toolName}。定制的通知让用户即使在其他应用中也能快速判断是否需要立即回来。三、权限规则管理事后可控3.1 /permissions 命令权限规则不仅可以在对话中实时创建还可以通过/permissions命令进行事后管理allow ─ ask ─ deny │ workspace ─ recent ───────────────────────────────────────── Bash(npm run:*) localSettings Bash(git:*) projectSettings Read userSettings Edit(*.md) session [a] Add rule [d] Delete rule [/] Search五个标签页覆盖了权限管理的所有维度allow/ask/deny查看和管理三种行为的规则workspace管理额外的工作目录recent查看 Auto 模式最近拒绝的命令支持快速授权3.2 规则保存位置的选择添加新规则时系统询问保存位置位置范围Git 跟踪场景全局userSettings—个人习惯如总是允许git:*项目级projectSettings✅ 提交到 Git团队共享规则项目级localSettings❌ gitignore个人的项目特定规则会话级session—临时规则projectSettings 和 localSettings 的分层让个人偏好与团队规范共存——projectSettings.claude/settings.json提交到 Git团队成员共享localSettings.claude/settings.local.json被 gitignore存放个人偏好。3.3 阴影规则检测当用户添加新的 allow 规则时系统调用detectUnreachableRules()检测是否被已有的 ask/deny 规则遮蔽。例如如果用户添加Bash(rm:*) → allow但已经存在Bash(rm:*) → deny系统会警告“此规则被优先级更高的 deny 规则遮蔽不会生效。”这防止了一个常见的用户困惑“我明明添加了规则为什么不生效”四、多 Agent 场景的权限处理4.1 子 Agent 的权限继承当主 Agent 派生子 Agent 执行任务时详见第6章权限如何传递Claude Code 的策略是子 Agent 继承父 Agent 的权限上下文包括所有规则和当前模式子 Agent 不能自行提升权限级别子 Agent 的权限请求会上报到主会话的 UI 中这三个规则形成了一个权限下降原则——权限只能被继承或限制不能被提升。这防止了子 Agent 通过委托来绕过父 Agent 的权限限制。4.2 Worker Badge当权限请求来自子 Agent 时对话框标题区域显示 Worker 徽章Bash test-runner npm test徽章标识了权限请求的来源——是主 Agent 自己发出的还是某个子 Agent 的请求。这让用户可以基于上下文做出更明智的决策——例如一个名为test-runner的子 Agent 执行npm test可能比主 Agent 直接执行更可信。五、设计模式提炼第5章蕴含了 8 个可复用的设计模式值得系统梳理模式一判别联合 决策溯源问题决策系统需要返回不同类型的结果且每个结果需要携带不同的附加信息方案使用 TypeScript 判别联合类型按 behavior 字段区分每种变体携带特定的附加数据。同时为每个决策附加 decisionReason形成完整的审计链适用场景任何需要可解释性的决策系统——权限、审批、推荐引擎模式二分级配置合并Layered Config Merge问题配置来自多个来源个人、团队、企业可能存在冲突方案定义明确的来源优先级序列按来源分组存储规则而非扁平化支持策略级别的全局覆盖适用场景IDE 插件、CLI 工具多层 dotfile、企业 SaaS模式三快路径/慢路径分离问题AI 分类需要兼顾延迟和准确性方案Stage 1 用极少的 token 做快速判断只有不确定时才进入 Stage 2 的深度推理适用场景任何需要 LLM 辅助决策的系统——内容审核、意图识别、异常检测模式四断路器防 AI 卡死问题AI 分类器可能在某些上下文下产生系统性偏见反复拒绝合理操作方案双重计数器连续拒绝 总计拒绝任一超限则回退到人工确认适用场景任何依赖 AI 自动决策的系统模式五变换函数防竞态问题异步操作的结果需要应用到可变状态但状态可能在异步期间被修改方案异步操作返回变换函数而非快照应用时基于最新状态计算适用场景React setState、任何异步状态更新场景模式六危险权限的剥离-恢复问题某些模式下需要临时限制用户配置的宽泛权限但不应永久删除方案进入限制模式时将危险规则移入暂存区退出时恢复适用场景需要在不同安全级别间切换的系统模式七渐进式信任 UX问题安全确认框太多会导致权限疲劳太少会丧失安全性方案提供多级信任选项一次性 → 模式化 → 持久化让用户逐步建立信任关系。可编辑规则让用户参与安全决策适用场景任何需要用户审批的 CLI/GUI 工具模式八纵深防御漏斗问题单一安全检查不够但多层检查的延迟如何控制方案按成本从低到高排列检查层每一层只处理上一层无法判定的请求形成漏斗式过滤适用场景任何需要多层安全检查的系统——WAF、API 网关、权限网关模式速查表模式核心思想本章位置判别联合 决策溯源决策结果携带类型特定数据和原因链5.2分级配置合并多来源按优先级分组存储、策略覆盖5.3快路径/慢路径分离简单情况走快路径复杂情况走慢路径5.6断路器模式连续失败超限则自动回退5.7变换函数防竞态异步操作返回函数而非快照5.8剥离-恢复模式临时限制配置退出时恢复5.8渐进式信任 UX多级信任选项 用户参与规则制定5.9纵深防御漏斗按成本逐层过滤每层只处理上游无法判定的请求全章横向对比三大框架的权限模型维度Claude CodeLangChainOpenAI Agents SDK权限粒度工具级 命令级 路径级工具级输入/输出级GuardrailsAI 分类器✅ YOLO Classifier❌❌规则持久化4 级存储全局/项目/本地/会话无无模式切换6 种模式 ShiftTab全局开关开/关UX 定制每种工具专属权限 UI通用确认框通用确认框安全免疫危险路径不受 bypass 影响无无子 Agent 权限继承 不可提升无无Claude Code 的独特之处AI 分类器集成——用 LLM 的语义理解补充规则引擎的覆盖盲区8 层优先级——支持从个人到企业的完整治理链工具专属 UX——每种工具有定制的权限交互界面安全免疫设计——关键路径不受 bypass 模式影响权限下降原则——子 Agent 只能继承或受限不能提升这些特性使得 Claude Code 的权限系统不只是一个简单的门控层而是一个完整的Agent 治理框架。实战启示启示一安全与体验不是零和博弈越安全越难用是一个常见但错误的假设。Claude Code 的渐进式信任设计证明了安全性和流畅性可以共存Bash 四选项让用户在一次性放行和持久化规则之间有精细选择可编辑前缀规则让用户从被动审批者变为主动规则制定者Diff 驱动决策展示用户关心的信息变更内容而非技术细节sed 语法Tab 反馈让高级用户有更多控制但不增加初级用户的认知负担核心原则好的安全 UX 不是减少确认框而是让每个确认框都有价值。启示二权限下降原则是 Agent 治理的基石子 Agent 只能继承或受限权限不能提升——这个单向阀防止了一系列攻击子 Agent 不能通过委托绕过父 Agent 的权限限制子 Agent 不能自主切换到更宽松的权限模式即使子 Agent 被恶意 Prompt 注入其破坏范围也被权限边界限制核心原则权限的传播方向应该是从高到低——从用户到 Agent从父 Agent 到子 Agent。永远不应该有逆向提升的路径。启示三剥离-恢复优于删除-重建Auto 模式临时剥离危险规则退出时恢复——而非直接删除让用户重新配置。这个设计选择体现了一个重要的工程原则对用户配置的修改应该是可逆的。删除-重建的问题在于用户可能记不住之前的配置重建可能引入错误或遗漏。而剥离-恢复确保了用户的原始意图不被丢失——即使他们频繁在模式之间切换。下期预告第09篇让 AI 带 AI——Fork-and-Delegate 多 Agent 协作架构全解第5章权限深潜到此结束接下来进入阶段四协作与上下文篇第09篇将深入第6章前半6.1-6.5Fork-and-Delegate——轻量级并行分叉的机制与权衡Agent Type Registry——动态类型注册的扩展性设计Tool Sandboxing——子 Agent 的工具权限隔离Scoped Memory——分区化上下文记忆防止信息串扰从一个 Agent 的权限到多个 Agent 的协作架构的复杂度将再次跃升。