Skip to content

dsh 中的权限是两个正交旋钮,由一个预设捆绑:沙箱模式read-only / workspace-write / danger-full-access,来自沙箱架构)与审批策略ask / never)。"什么需要审批"由组合这两者决定,而非一张平坦的工具名允许列表。

角色
packages/interaction/permission-presetsctx.permissionPresets:预设表、/permission 命令、permissions 投影
packages/interaction/user-approvalctx.approval:请求/取消/决定、会话策略、审计事件
packages/core/toolsctx.tools:解决审批 ask 的 pre-execute/execute/post-execute 管线
packages/client/ui-permission-presetsWeb 表面:新会话 Settings 行 + 当前会话命令选择器
packages/guard/repeat-tool-reminder提示模型重复调用的咨询性提示

什么需要审批

审批围绕 ApprovalPolicypackages/interaction/user-approval/src/index.ts)构建:

  • 'ask'(默认)——委托给组合好的应答器;没有组合任何应答器时,链以 'unavailable' fail-closed。
  • 'never'——绝不提示任何人;每次请求都确定性地解析为 'rejected'(严格的无头/CI 立场)。

因此不存在固定的"危险工具"列表。相反,当 (a) 工具的门返回 ask,或 (b) 沙箱升级路径(sandbox_permissions + justification)被调用时,请求就需要审批——两者都汇聚到同一个 ctx.approval。在 workspace-write 下,工作区之外的更宽写入或 shell 命令必须升级,从而提示用户;在 danger-full-access + never 下,什么都不提示。预设即部署指向其中某一种组合的方式。

预设名与捆绑

预设表(packages/interaction/permission-presets/src/index.ts)把名字映射为 PresetSpec——一个 sandbox 模式加上一个 approval 策略(以及可选展示用的 name/description)。内置默认:

预设名sandboxapproval
workspace-writeworkspace-writeask
danger-full-accessdanger-full-accessnever

保留名 customCUSTOM_PRESET)绝不是切换目标——它是在有效旋钮值不匹配任何表项时的派生状态。表可通过插件 Config.presets 配置;服务构造函数要求一个会隔离ctx.shell(暴露 sandboxMode 的那个)以及 ctx.approval——在未隔离的执行器上组合预设是加载期错误。

切换预设会记录持久化、仅日志的用户意图,然后通过规范性 setter 写每个变更的旋钮:

  • 选中名字的 permission/preset 事件(session.append);
  • setSandboxMode(session, spec.sandbox)——发出 sandbox/mode@deepseek-ai/dsh-sandbox-policy);
  • setApprovalPolicy / ctx.approval.setPolicy(agent, policy)——发出 approval/policy

新会话会被固定(session/createdpinInitialPermission)到当前用户默认预设(defaultPreset),其中真正全新的会话从注册到 settings seam(permission 命名空间)的 defaultSettings 源获得整个捆绑。

工具管线中的权限模型

决策点位于 ctx.toolstools/pre-execute waterfallpackages/core/tools/src/index.ts);规范化顺序记录在官方文档的工具执行管线一章(源码:docs/tool-execution-pipeline.md)。交叉对照该管线:

text
tools/pre-execute waterfall   (hooks、权限、沙箱)
   → 门:allow | deny | ask
   → ask → ctx.approval 一次性提示(缺失/无法应答 → deny)
单调守卫
tools/execute waterfall       (timeout、retry、metrics——围绕派发)
工具 body
tools/post-execute waterfall

相关门类型是 { kind: 'ask'; reason?: string }。返回 ask 的门由 tools 服务的私有方法 serviceAskpackages/core/tools/src/index.ts)解决,后者用 ctx.get('approval') 机会式消费:

  • 没有挂载审批服务 → ask 退化为 denytool "…" requires approval (not yet supported));
  • 调用上没有 agent → deny… no agent to route it through);
  • 否则调用 approval.request({ agent, toolName, callId, reason, signal }) 并把结果映射为:allowed-once → allowrejected → denycancelled → deny(带 approvalCancelled);unavailable → deny

被拒的调用跳过工具 body 并流入 tools/post-execute,因此监听器顺序无法把拒绝再变回权限。

沙箱升级路径

sandbox 工具家族(tool-fstool-bash)对升级使用同一审批接口。approveEscalationpackages/sandbox/sandbox/src/escalation.ts)是共享编排:

  1. 对照调用有效模式的严格更宽检查(WIDER_MODESread-only → workspace-write → danger-full-access);
  2. 要求已挂载的审批服务与调用方 agent(否则以逐字错误文本 fail-closed);
  3. 调用 approval.request({ agent, toolName, callId, reason: 'escalate sandbox to <mode>: <justification>', signal })
  4. 把封闭结果映射为恰好本次调用的授权模式。

模型面向的词汇是 sandboxDenialMarker(mode)[sandbox: file access denied under <mode> mode])与 escalationHintMarker(subject),另加 validateEscalationArgs 强制 sandbox_permissionsjustification 同来同去。

审批服务本身

ApprovalServicepackages/interaction/user-approval/src/index.ts)在应答器之前应用会话策略,并记录每次询问/结果对:

  • 事件approval/request(waterfall,带作用域过滤),以及仅日志的审计事件 approval/asked(id、toolName、callId、reason)与 approval/decided(id、outcome)。
  • 结果ApprovalOutcometypes.ts):'allowed-once' | 'rejected' | 'cancelled' | 'unavailable''allowed-once'唯一的授权——授权是一次性的,只适用于被请求的动作,绝非持久的会话级放行。
  • 会话包裹审计request() 要求会话日志中存在一个未闭合的轮次(turn/start 尚未被 turn/end 闭合),因为在轮次之间的裸事件会在重载时成为崩溃尾部垃圾。它追加 approval/asked,解析 decide,再追加 approval/decided
  • 'never' 静音在服务自身的请求路径内决定,而非由监听器决定,因此一个前插的监听器永远无法绕过文档化的"never → 确定性地 rejected"。
  • 包含(containment):抛错的应答器会让问题 fail-closed(解析为 unavailable),而非让调用方工具失败;AbortSignal 与应答竞速,产出 cancelled

会话/全局作用域:策略按会话(approval/policy 事件折叠,或配置默认),而每次授权按请求(allowed-once)。审计事件位于请求 agent 的会话日志上,因此恢复会重放同一对 ask/decide。

审批与沙箱模式的会话事件

事件载荷含义
approval/asked{ id, toolName, callId?, reason? }向应答链提出一个问题(审计)
approval/decided{ id, outcome }每次询问恰好一个:决定、取消或 fail-closed
approval/policy{ policy, source? }会话审批策略覆盖(持久化、可重放;source: 'delegation' 为子会话播种)
sandbox/mode{ mode, source? }会话沙箱模式覆盖(由 sandbox-policy 折叠)
permission/preset{ preset }所选预设(持久化仅日志意图)

Web 表面

ui-permission-presetspackages/client/ui-permission-presets)是一个表面插件:其宿主 apply() 是空操作;浏览器半区提供新会话 Settings 行与当前会话命令选择器(服务贡献的 /permission 命令)。它通过 displayPermissionPreset(value, name) 渲染每个预设,为 danger-full-access 预设(FULL_ACCESS_PRESET)保留产品化标签——"Full access"。客户端提交的机器值正是服务所解析的预设名。

延伸阅读

  • 沙箱架构:总览——本审批模型所管控的文件影响模式与升级阶梯。
  • 文件系统观察与沙箱策略——在下一次调用中,被授予的更宽模式如何被 fs 围栏兑现。
  • 守卫:超时与重复提醒——pre-execute 门两侧的 tools/execute/tools/post-execute 管线。
  • packages/interaction/user-approval/src/index.ts——ApprovalService、结果与审计事件对。
  • packages/interaction/permission-presets/src/index.ts——PermissionPresetService、预设表与 /permission 命令。
  • packages/core/tools/src/index.ts(搜索 serviceAsk / PreToolDecision)——ask 门在哪里被变成决定。