yyyyolo7a79-sketch
dsh-auto-and-safety
DSH auto-and-safety permission preset: full file access but no delete without user approval
- Stars
- 2
- Language
- JavaScript
- Created
- Aug 15, 2026
- Updated
- Aug 15, 2026
Introduction
dsh-auto-and-safety 权限插件
为 DeepSeek Harness (DSH) 新增一个
auto-and-safety权限预设:继承danger-full-access(全文件权限)的能力,但禁止一切删除文件的行为;每次任务完成后,agent 必须向用户询问是否删除任务过程中产生的临时(tmp)/ 冗余文件,指明文件是什么、文件位置,经用户明确批准后才可删除。
目录
- 1. 需求背景
- 2. 总体设计
- 3. DSH 权限体系原理(前置知识)
- 4. 实现细节
- 5. 安装步骤
- 6. 使用流程(与演示截图对应)
- 7. 实现效果
- 8. 行为示例(拦截记录)
- 9. 已知限制与边界
- 10. 卸载方法
- 11. 给 dsv4 / 后续整合者的说明
1. 需求背景
用户原始需求(原话):
在你的权限
read only / workspace write / all accuse中再加一条权限auto-and-safety: 内容为继承 all accuse 的权限,但是禁止一切删除文件的行为,每次任务完成后向用户询问是否删除任务过程中产生的 tmp、冗余文件,并指明文件是什么以及文件位置,在用户批准后才可以删除。
即:希望 agent 拥有"全权限"干活能力的同时,把"删除文件"这一不可逆高危操作锁死为人批后放行,防止 agent 自行清理/误删。
2. 总体设计
实现分两层,缺一不可:
| 层 | 载体 | 作用 |
|---|---|---|
| 权限预设层 | cordis.patch.yml 中 permission 行的 presets 表 | 让 auto-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-policy 的 SANDBOX_MODES | read-only / workspace-write / danger-full-access |
审批策略 ApprovalPolicy | @deepseek-ai/dsh-user-approval 的 APPROVAL_POLICIES | ask / never |
| 沙箱升级阶梯 | @deepseek-ai/dsh-sandbox 的 WIDER_MODES | read-only → workspace-write → danger-full-access(只升不降) |
3.2 权限预设表(permission presets)
- 插件:
@deepseek-ai/dsh-permission-presets(行 id:permission,位于dsh-basebundle)。 - 配置:
presets是一个 dict,每项{ sandbox, approval, name?, description? }。 - 切换机制:切换预设会向会话日志追加
permission/preset事件,并分别写sandbox/mode、approval/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-base中permission行的默认 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-execute,prepend: 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 手动安装
- 放插件源码:把
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
- 例如:
- 改配置:把
cordis.patch.yml中的两个条目(- id: permission与- insert:)并入<DSH_HOME>/profiles/<profile名>/cordis.patch.yml。- 若你的 patch 层已有
- id: permission条目,用新 presets 覆盖即可(注意写全四个预设)。
- 若你的 patch 层已有
- 生效:
- 配置走 HMR 热重载,通常无需重启;若 GUI 权限选择器未出现新选项,重启 web。
- 验证组合:
dsh --profile web --dump-config,应能看到auto-and-safety预设与插件行。
- 启用:在会话中执行
/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 |
| 5 | agent 重试删除命令 → 拦截器检测到最近用户消息含批准短语 → 放行,删除成功 | docs/批准后成功删除.png |
演示截图
① 权限选择器:新增的 auto-and-safety 预设(当前已选中)

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

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

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

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.md、plugin-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. 已知限制与边界
- shell 命令拦截无法穷举所有删除形态:正则覆盖常见删除命令(Remove-Item/rm/del/erase/rmdir/rd/unlink/shred/Empty-RecycleBin/.NET ::Delete),但理论上仍存在变体(脚本文件内部删除、编码混淆、外部程序调用等)。设计上"宁可误拦、不可漏放",且 prompt 规范 + 拦截双重约束,实际已覆盖 99% 场景。
- 一次批准放行窗口:拦截器以"最近一条用户消息是否含批准短语"为准,用户批准后到下一次用户消息之前的所有删除命令都会被放行。因此应引导 agent 一次性列出全部待删文件问清,避免逐文件多次询问。
- 子代理(subagent)会话:子代理会话默认走默认预设(
workspace-write),拦截不直接作用于子代理的 shell 调用;但父会话的 prompt 规范会传导,行为上仍受约束。 - 审批策略旋钮:预设的
approval: ask使会话审批策略为 ask;在danger-full-access下沙箱无升级需求,ask 主要保留审批通道与提示语义,不产生额外弹窗。 - HMR 依赖:配置热重载依赖 web 的 HMR 服务;极端情况下(插件文件损坏、配置 YAML 语法错误)会导致 loader 报错,此时重启 web 并按报错修复。
10. 卸载方法
- 从
cordis.patch.yml删除- id: auto-and-safety的insert条目(可保留permission行的 presets,去掉auto-and-safety一项即可恢复三预设)。 - 删除
plugins/auto-and-safety/目录。 - 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/dsh0.1.0-rc.6。升级 DSH 后若权限体系接口变化(SANDBOX_MODES/APPROVAL_POLICIES/tools/pre-execute/permissionPresets.current()),按新源码调整。 - 可扩展方向:
- 把"批准短语"做成插件 Config(
z.object({...})),允许用户自定义放行词表; - 增加"删除前自动询问"(不 deny 而是转 ask_user_question)模式;
- 将拦截扩展到
run_code语言派发(如 Pythonos.remove)等更多删除形态。
- 把"批准短语"做成插件 Config(