yyyyolo7a79-sketch / dsh-auto-and-safety

Listed

DSH auto-and-safety permission preset: full file access but no delete without user approval

mainOther View source

Installation

npx -y @deepseek-ai/dsh plugin --profile web add github:yyyyolo7a79-sketch/dsh-auto-and-safety

This installation command is an unverified starting point generated from the GitHub repository address.

README

Maintainer-authored documentation snapshot.

View on GitHub ↗
Commit 06deceaSynced Aug 18, 2026

dsh-auto-and-safety 权限插件

为 DeepSeek Harness (DSH) 新增一个 auto-and-safety 权限预设:继承 danger-full-access(全文件权限)的能力,但禁止一切删除文件的行为;每次任务完成后,agent 必须向用户询问是否删除任务过程中产生的临时(tmp)/ 冗余文件,指明文件是什么、文件位置,经用户明确批准后才可删除


目录


1. 需求背景

用户原始需求(原话):

在你的权限 read only / workspace write / all accuse 中再加一条权限 auto-and-safety: 内容为继承 all accuse 的权限,但是禁止一切删除文件的行为,每次任务完成后向用户询问是否删除任务过程中产生的 tmp、冗余文件,并指明文件是什么以及文件位置在用户批准后才可以删除

即:希望 agent 拥有"全权限"干活能力的同时,把"删除文件"这一不可逆高危操作锁死为人批后放行,防止 agent 自行清理/误删。


2. 总体设计

实现分两层,缺一不可:

载体作用
权限预设层cordis.patch.ymlpermission 行的 presetsauto-and-safety 成为可切换的权限预设(GUI 选择器 + /permission 命令可见可选)
行为强制层本地插件 plugins/auto-and-safety/lib/index.js① 向系统提示注入安全规范;② 在 tools/pre-execute 工具执行门上硬拦截删除命令

设计要点:

  • 预设 = 两个旋钮的组合:DSH 的权限预设表每项只有 sandbox(文件沙箱模式)和 approval(审批策略)两个字段。auto-and-safety = sandbox: danger-full-access(继承 all access)+ approval: ask(保留审批通道)。
  • "禁止删除"无法用旋钮表达:DSH 沙箱模式枚举只有 read-only / workspace-write / danger-full-access,没有"禁止删除"模式。因此"禁止删除 + 批准后放行"必须落在工具执行前的拦截钩子上。
  • 官方 fs 工具本身没有删除能力:DSH 的 fs 工具只有 read / write / edit / read_image,删除文件只能通过 shell(pwsh / bash)命令或 .NET 调用。所以拦截点聚焦 shell 命令即可覆盖实际删除路径。

3. DSH 权限体系原理(前置知识)

以下基于 @deepseek-ai/dsh rc.6 源码,整合时如版本变化请以实际源码为准。

3.1 三个硬编码枚举

概念定义位置取值
沙箱模式 SandboxMode@deepseek-ai/dsh-sandbox-policySANDBOX_MODESread-only / workspace-write / danger-full-access
审批策略 ApprovalPolicy@deepseek-ai/dsh-user-approvalAPPROVAL_POLICIESask / never
沙箱升级阶梯@deepseek-ai/dsh-sandboxWIDER_MODESread-only → workspace-write → danger-full-access(只升不降)

3.2 权限预设表(permission presets)

  • 插件:@deepseek-ai/dsh-permission-presets(行 id:permission,位于 dsh-base bundle)。
  • 配置:presets 是一个 dict,每项 { sandbox, approval, name?, description? }
  • 切换机制:切换预设会向会话日志追加 permission/preset 事件,并分别写 sandbox/modeapproval/policy 事件;会话通过日志回放(fold)恢复当前预设,因此重启不丢状态。
  • 派生逻辑 derive():按"当前折叠的 sandbox + approval 值"匹配表项;匹配不到显示为 custom
  • custom 是保留名,不能用作家表项名。
  • 默认预设推断:无显式 defaultPreset 时取组合默认值(workspace-write + ask)能匹配到的表项。

3.3 组合补丁机制(patch)

  • DSH 的 profile 组合 = 多个 bundle 的 cordis.patch.yml 按顺序叠加 + 用户 patch 层(<DSH_HOME>/profiles/<profile>/cordis.patch.yml)。
  • patch 条目语法(顶层 YAML 数组):
    • - id: <行id> + 其余字段 → 整行覆盖目标行的对应字段(config 整体替换,不是合并);
    • - insert: [...] → 插入新行(顶层或指定组);
    • - id: <行id> + disabled: true → 禁用。
  • dsh-basepermission 行的默认 presets 只有三个:read-only / workspace-write / danger-full-access
  • 用户 patch 层被 HMR 监听,修改后热重载,无需重启 web。

3.4 工具执行前拦截门(tools/pre-execute waterfall)

  • 每个工具调用执行前,工具注册表跑一个 waterfall:ctx.waterfall(carrier, 'tools/pre-execute', exec, () => ({ kind: 'allow' }))
  • 任何插件可用 ctx.on('tools/pre-execute', (exec, next) => ...) 注册监听;返回 { kind: 'deny', reason } 即拒绝本次调用,工具结果会显示 Error: <reason>;返回 next() 放行。
  • exec 结构:{ name, arguments, agent, callId, signal, ... }exec.agent.session 是调用方会话,exec.arguments.command 是 shell 命令文本。
  • 语义:任一监听可 deny,且没有监听可以强制放行已被 deny 的调用。

3.5 系统提示动态注入(systemPrompt context)

  • ctx.systemPrompt.context({ name, order, text: (context) => string }) 注册动态提示段,text 每次构建提示时调用,可读 context.agent.session 按会话状态决定输出。
  • 参考:dsh-sandbox-policy 用 order 110 注入 "Current DSH file policy: ...";本插件用 order 111 注入安全规范。

4. 实现细节

4.1 预设表配置(cordis.patch.yml 片段)

- id: permission
  config:
    presets:
      read-only:
        sandbox: read-only
        approval: ask
      workspace-write:
        sandbox: workspace-write
        approval: ask
      danger-full-access:
        sandbox: danger-full-access
        approval: never
      auto-and-safety:
        sandbox: danger-full-access
        approval: ask
        name: auto-and-safety
        description: 继承 danger-full-access 的全部文件权限,但禁止一切删除文件的行为;任务完成后向用户询问是否删除任务产生的临时/冗余文件(指明文件与位置),批准后才可删除
- insert:
    - id: auto-and-safety
      name: ./plugins/auto-and-safety/lib/index.js

⚠️ 因为是整行替换,必须写全全部四个预设,漏掉任何一个都会使它在权限选择器中消失。

4.2 插件逻辑(plugins/auto-and-safety/lib/index.js)

纯 ES Module,无第三方依赖(不 import 任何包),导出 name / inject / apply,符合 Cordis 本地插件格式。

export const name = 'auto-and-safety';
export const inject = ['systemPrompt', 'permissionPresets'];

① 删除命令识别正则DELETE_CMD_RE,大小写不敏感,安全优先宁可误拦):

目标匹配
PowerShell cmdlet 与别名remove-item ri rm del erase rmdir rd empty-recyclebin
cmd 命令del erase rmdir rd
bash 命令rm rmdir unlink shred
.NET 静态调用[IO.File]::Delete(...)::delete\s*\(

rd 单独要求后随路径/开关(rd "路径" / rd /s ...),避免把参数里的目录名误判为删除命令。

② 批准放行判定GRANT_RE + QUESTION_RE):

  • 扫描会话事件(倒序),找最后一条 user/message 事件且 source.kind === 'user'(排除工具注入、系统消息);
  • 其文本命中批准短语 → 放行;命中疑问句([吗么呢??]\s*$)→ 视为询问而非批准;
  • 批准短语覆盖:"批准/同意/允许/确认(的|了)删除/删掉/移除/清理"、"请删除/请删掉"、"可以删除(了|吧)"、"删除吧/删掉吧/清理掉"等。

③ 拦截器tools/pre-executeprepend: true):

ctx.on('tools/pre-execute', (exec, next) => {
  if (exec?.name !== 'pwsh' && exec?.name !== 'bash') return next();
  const session = exec.agent?.session;
  if (!session || currentPreset(ctx, session) !== 'auto-and-safety') return next();
  const command = exec.arguments?.command ?? '';
  if (!DELETE_CMD_RE.test(command)) return next();
  if (hasUserGrant(session)) return next();
  return { kind: 'deny', reason: DENY_REASON };   // 拒绝,附流程指引
}, { prepend: true });

判定链:仅当 ①工具是 shell、②会话预设为 auto-and-safety、③命令命中删除模式、④最近用户消息无批准 → 拒绝。任何一步不满足都放行,因此不影响其他预设与其他工具。

④ 系统提示注入systemPrompt.context,order 111):

仅当会话预设为 auto-and-safety 时输出安全规范:禁止一切删除;任务完成后必须用 ask_user_question 列出文件(名称 + 完整路径 + 用途)询问;批准后才可删;无临时文件时明确说明。


5. 安装步骤

无论哪种安装方式,都必须手动合并 cordis.patch.yml 中的两个补丁条目(① - id: permission 预设表、② - insert: 挂载条目)——dsh 的 patch 是用户 profile 的配置层,安装命令不会代你改配置。合并时注意 permission 条目必须写全四个预设(整行替换语义,漏写会从权限选择器消失)。

5.1 快速安装(复制 GitHub 链接即可)

方式 A:git clone 到 plugins 目录(推荐,与 dsh-session-search 同款方式,挂载用本地路径)

# ① 克隆仓库到 profile 的 plugins 目录(以 web profile 为例)
git clone https://github.com/yyyyolo7a79-sketch/dsh-auto-and-safety.git <DSH_HOME>/profiles/web/plugins/dsh-auto-and-safety

# ② 把仓库根目录 cordis.patch.yml 的两个条目并入你的 patch 层:
#    <DSH_HOME>/profiles/web/cordis.patch.yml。
#    其中 - insert: 的挂载路径要写完整(克隆后仓库根在 plugins/dsh-auto-and-safety/,
#    插件本体在其内部的 plugins/auto-and-safety/lib/index.js):
#    - insert:
#        - id: auto-and-safety
#          name: ./plugins/dsh-auto-and-safety/plugins/auto-and-safety/lib/index.js

# ③ 验证生效(配置走 HMR 热重载,通常无需重启;若权限选择器无新选项则重启 web)
dsh --profile web --dump-config | grep auto-and-safety

方式 B:dsh plugin 命令安装(装进 profile 的 node_modules,pnpm 方式)

# ① 安装插件包(等价于在该 profile 目录执行 pnpm add;#main 可换成实际默认分支)
dsh plugin --profile web add github:yyyyolo7a79-sketch/dsh-auto-and-safety#main

# ② 同样把 cordis.patch.yml 两个条目并入,但 insert 的 name 改用包名(插件在 node_modules 里):
#    - insert:
#        - id: auto-and-safety
#          name: dsh-auto-and-safety

安装完成后,在会话中执行 /permission auto-and-safety(或在 GUI 会话权限选择器中选择)即可启用。

5.2 手动安装

  1. 放插件源码:把 plugins/auto-and-safety/ 整个目录复制到 <DSH_HOME>/profiles/<profile名>/plugins/ 下(与 dsh-session-search 等本地插件同级)。
    • 例如:C:\Users\PC\.dsh\profiles\web\plugins\auto-and-safety\lib\index.js
  2. 改配置:把 cordis.patch.yml 中的两个条目(- id: permission- insert:)并入 <DSH_HOME>/profiles/<profile名>/cordis.patch.yml
    • 若你的 patch 层已有 - id: permission 条目,用新 presets 覆盖即可(注意写全四个预设)。
  3. 生效
    • 配置走 HMR 热重载,通常无需重启;若 GUI 权限选择器未出现新选项,重启 web。
    • 验证组合:dsh --profile web --dump-config,应能看到 auto-and-safety 预设与插件行。
  4. 启用:在会话中执行 /permission auto-and-safety,或在 GUI 会话权限选择器中选择 auto-and-safety

6. 使用流程(与演示截图对应)

步骤动作截图
1权限栏出现新的 auto-and-safety 预设docs/权限栏新增.png
2任务完成后,agent 用 ask_user_question 列出临时文件(名称 + 路径 + 用途)询问docs/拦截演示1.png
3选择框选择 ≠ 批准,删除命令被硬拦截,要求对话内文字明确批准docs/拦截演示2.png
4用户对话回复"批准删除/可以删除/删除吧"等批准短语docs/拦截记录/session.jsonl
5agent 重试删除命令 → 拦截器检测到最近用户消息含批准短语 → 放行,删除成功docs/批准后成功删除.png

演示截图

① 权限选择器:新增的 auto-and-safety 预设(当前已选中)

权限栏新增

② 任务完成后,agent 列出临时文件询问用户是否清理(ask_user_question)

拦截演示1

③ 选择框选择不算批准,删除命令被硬拦截,要求对话内文字明确批准

拦截演示2

④ 用户在对话中回复"批准删除"后,删除执行成功

批准后成功删除


7. 实现效果

agent 视角(会话内)

  • 系统提示出现 auto-and-safety 专属规则段(order 111),agent 从第一轮起就知道:

    • 禁止一切删除文件/目录的行为;
    • 任务完成后必须主动列出 tmp/冗余文件(名称 + 完整路径 + 用途)并询问;
    • 只有用户明确批准后才可删除;没产生临时文件时明确说明"无临时文件需要清理"。
  • 删除命令(shell / .NET)在工具执行门被硬拦截,返回:

    Error: auto-and-safety 权限预设禁止直接删除文件/目录:此命令包含删除操作且本会话尚未获得用户批准,已拦截。正确流程:先用 ask_user_question 向用户列出要删除的每个文件(名称 + 完整路径 + 用途/是什么),征得用户批准;用户回复明确的批准(如"批准删除""同意删除""可以删除""删除吧")后,再重试删除该文件的命令。不得用脚本、编码或其他命令变体规避拦截。
    
  • 拦截提示会引导 agent 走"询问 → 批准 → 重试"闭环,而不是绕过。

用户视角

  • 权限选择器//permission 多一个 auto-and-safety 选项,切换即时生效;
  • 每次任务完成后收到明确的清理询问(文件是什么、在哪、要不要删);
  • 删除永远发生在自己点头之后。

8. 行为示例(拦截记录)

以下为真实会话回放(docs/拦截记录/session.jsonl,DSH 会话日志)的完整「拦截 → 询问 → 批准 → 放行」闭环。

场景:搭建 deepseek + dsh 工作流时,agent 批量安装 12 个目标插件(最终装 8 个插件 + 5 个技能),任务完成后产生约 20MB 临时文件(readme_dsh_plugins.mdplugin-install\ 研究目录等),触发 auto-and-safety 清理流程。

阶段事件要点
任务执行安装插件与技能,全程未触碰 .agents / .claude 目录产生临时文件:1 个 94KB README + 安装研究目录(12 份文档 + 10 个 git 克隆)
① 拦截agent 尝试直接清理临时文件删除命令在 tools/pre-execute 被硬拦截,返回 Error 并提示正确流程
② 询问agent 改用 ask_user_question 列出待删文件(名称 + 完整路径 + 用途)选择框点击不算批准——工具层未收到批准标记
③ 引导agent 说明规则,请用户在对话内文字明确批准"我不会绕开这个保护,请你直接回复确认"
④ 批准用户回复「批准删除」命中批准短语(GRANT_RE),授权生效
⑤ 放行agent 重试删除命令拦截器检测到最近用户消息含批准短语 → 放行
⑥ 完成删除成功并验证仅删除批准的两个对象,工作区其余文件未动

完整对话回放见 docs/拦截记录/session.jsonl(JSONL 格式,可用 DSH 会话查看器打开)。


9. 已知限制与边界

  1. shell 命令拦截无法穷举所有删除形态:正则覆盖常见删除命令(Remove-Item/rm/del/erase/rmdir/rd/unlink/shred/Empty-RecycleBin/.NET ::Delete),但理论上仍存在变体(脚本文件内部删除、编码混淆、外部程序调用等)。设计上"宁可误拦、不可漏放",且 prompt 规范 + 拦截双重约束,实际已覆盖 99% 场景。
  2. 一次批准放行窗口:拦截器以"最近一条用户消息是否含批准短语"为准,用户批准后到下一次用户消息之前的所有删除命令都会被放行。因此应引导 agent 一次性列出全部待删文件问清,避免逐文件多次询问。
  3. 子代理(subagent)会话:子代理会话默认走默认预设(workspace-write),拦截不直接作用于子代理的 shell 调用;但父会话的 prompt 规范会传导,行为上仍受约束。
  4. 审批策略旋钮:预设的 approval: ask 使会话审批策略为 ask;在 danger-full-access 下沙箱无升级需求,ask 主要保留审批通道与提示语义,不产生额外弹窗。
  5. HMR 依赖:配置热重载依赖 web 的 HMR 服务;极端情况下(插件文件损坏、配置 YAML 语法错误)会导致 loader 报错,此时重启 web 并按报错修复。

10. 卸载方法

  1. cordis.patch.yml 删除 - id: auto-and-safetyinsert 条目(可保留 permission 行的 presets,去掉 auto-and-safety 一项即可恢复三预设)。
  2. 删除 plugins/auto-and-safety/ 目录。
  3. HMR 自动生效或重启 web。

11. 给 dsv4 / 后续整合者的说明

  • 文件清单
    • plugins/auto-and-safety/lib/index.js — 插件本体(唯一代码文件,零依赖)
    • cordis.patch.yml — 配置补丁(预设表 + 插件挂载)
    • README.md — 本文档
    • docs/需求.md(原始需求)、4 张演示截图、拦截记录/session.jsonl(演示会话回放)
  • 整合最小改动:复制插件目录 + 合并两个 patch 条目,改改 description 文案即可。
  • 版本兼容:基于 @deepseek-ai/dsh 0.1.0-rc.6。升级 DSH 后若权限体系接口变化(SANDBOX_MODES / APPROVAL_POLICIES / tools/pre-execute / permissionPresets.current()),按新源码调整。
  • 可扩展方向
    • 把"批准短语"做成插件 Config(z.object({...})),允许用户自定义放行词表;
    • 增加"删除前自动询问"(不 deny 而是转 ask_user_question)模式;
    • 将拦截扩展到 run_code 语言派发(如 Python os.remove)等更多删除形态。

Project files and signals

Shown items are public repository signals detected in the directory snapshot.

DocumentationDetected

Repository information

Language
JavaScript
License
MIT
Last updated
Aug 15, 2026, 4:08 AM

Install deliberately

Review source code, permissions, lifecycle hooks, dependencies and network access. Test untrusted plugins in an isolated environment.