dsh-root-temp-guard:把「临时文件进 tmp」从约定变成执行期拦截

前言

在 DeepSeek Harness(DSH)里跑智能体时,工具层可以写文件、跑 shell。很多仓库会在 AGENTS.md 或团队规范里写一句:临时目录、日志、探针文件不要堆在仓库根目录,统一放进 tmp/<任务名>/。规范写在文档里,模型或脚本仍可能在根目录创建 _tmp_probe/、build.log、.tmp 等条目,事后要靠人工或 CI 清理。

dsh-root-temp-guard 是 DSH Host 侧的工作流类插件,在工具调用管线里拦截「在工作区根目录创建临时目录 / 临时文件」,并可在回合结束时把漏网的根目录临时项移入隔离目录。下面介绍它的定位、三层机制、安装方式与配置要点。

这是什么

维护者 lizhijie429,仓库 lizhijie429/dsh-root-temp-guard,npm 包名 dsh-root-temp-guard(资料中版本 0.6.0),许可证 MIT。DSH 引擎要求:>=0.2.1-0 <0.3.0。

一句话定位:在 Host 插件层把「临时产物统一放临时目录」的项目约定,从文字规则升级为执行期拦截;工作区根目录随会话解析(session-root aware),规则与文案可配置。

务必区分边界:这是卫生 / 约定守卫,不是安全边界。文件类工具走参数解析与 fs-intent 两层判定,命令类只做启发式文本匹配;恶意或复杂 shell 仍可绕过,真正边界仍是 DSH 沙箱。

社区目录页:SkillHub 插件页(与 DeepSeek / 幻方无官方从属,属社区收录)。

核心功能:三层拦截

插件 README 将机制归纳为三层,且三层在插件内部异常时均为 fail-open(放行并写日志,不卡死工具调用)。

1、工具参数层

通过 ctx.tools.guard() 在工具执行前运行,结合工具参数与会话当前工作目录(workingDirectory.get(session))判定。即便模型先 working_directory { cd: "tmp" } 再写 ../_tmp_x,也会按真实 cwd 解析并拦截。

2、已解析目标层

监听 DSH 的 fs/write-intent / fs/edit-intent waterfall(write、edit、str_replace_editor 均会触发),在路径解析完成、写盘前判定。该层使用 realpath 身份,因此通过 tmp/link 等 junction/symlink 把根目录临时项藏进允许的 tmp/ 也会被拒绝。

3、回合结束清扫

监听 session/event 的 turn/start / turn/end:把本轮新出现且名字像临时项的根目录条目移动(不删除)到 tmp/_quarantine/<timestamp>-<name>,便于人工恢复。这是对命令类启发式绕过的兜底;turn/start 之前已存在的条目不会被被动处理。

默认覆盖的工具类型(资料原文):

  • 文件类:write、edit、str_replace_editor(view 只读放行)
  • 命令类:pwsh、bash、terminal_send(解析 command / text;workdir 按当前 cwd,cd / Set-Location 后的相对路径按新目录解析)

默认规则示例:根下的 _tmp_probe/、scratch/、test-out/、_tmp_notes.md、build.log、x.tmp 等会拒绝;配置的统一临时目录(默认名 tmp)下的路径允许;工作区外路径不干预,交给沙箱策略。

安装与启用

本插件是 DSH profile 组合包(bundle):package.json 的 dsh.bundle.patch 指向 cordis.patch.yml,安装后 DSH 会自动插入 root-temp-guard 行(默认 tmpDirName: tmp、quarantine: strict)。

CLI profile(推荐)

npm 发布后,在 web / headless 等由 CLI 管理的 profile 上执行:

dsh plugin --profile web add dsh-root-temp-guard

安装后可用下面命令确认 patch 是否生效:

dsh --profile web --dump-config | Select-String -Context 0,6 'dsh-root-temp-guard'

发布前从本地目录安装(资料示例,无构建步骤):

dsh plugin --profile web add "file:$env:USERPROFILE\.dsh\plugins\dsh-root-temp-guard"

桌面端 profile

desktop profile 由 Electron 管理,dsh plugin 不直接改它。需在 %USERPROFILE%\.dsh\profiles\desktop\cordis.patch.yml 末尾按绝对路径插入 root-temp-guard 条目(字段见 README「方式二」)。纯 config 改动在默认 patchReload: live 下可热生效;改 index.mjs / lib/guard.mjs 可能受 ESM 缓存影响,需重启 DeepSeek Harness。

临时验证(不改 profile)

dsh headless --patch <含 root-temp-guard insert 的 yaml> "hello"

安装插件会以当前 DSH 进程权限运行 Host 代码;启用前应自行查看仓库源码与 MIT 许可证,确认符合你的环境策略。

典型配置

config: 块全部可选。常见字段如下(默认值以 README 为准):

字段 默认 说明
enabled true 总开关
mode deny deny 拒绝;warn 只记 would-deny 并放行(灰度)
tmpDirName tmp 统一临时目录名(单路径段)
allowRootNames [] 额外放行的根目录名
quarantine strict 回合结束清扫;off 关闭
reasonTemplate 见 README 拒绝文案,支持 {name}、{tmpDir}

在 profile 的 cordis.patch.yml 里按 id: root-temp-guard 覆盖 config 会整体替换该行的 config。资料中的灰度示例:

- insert:
    - id: root-temp-guard
      name: 'C:\Users\<你>\.dsh\plugins\dsh-root-temp-guard\index.mjs'
      config:
        mode: warn
        tmpDirName: scratchpad
        reasonTemplate: '临时产物请写入 {tmpDir}/<任务名>/,不要污染仓库根目录:{name}'

日志默认写在 <插件目录>/state/root-temp-guard.log,超过 5MB 会轮转一次为 .1。拒绝、隔离移动、挂载/dispose 等事件会写入该文件。

适用场景与注意

适合谁

  • 已在 AGENTS.md 或团队规范里约定「临时文件进 tmp/」,希望 Host 层自动挡掉根目录 _tmp*、.log、.bak 等常见污染。
  • 需要先用 mode: warn 观察误报,再切 deny 上线的工作流团队。

不要指望它做什么

  • README「已知限制」明确:node -e "fs.mkdirSync(...)"、复杂拷贝、管道、别名、脚本间接建目录等可绕过命令类启发式。
  • 无 session 的 agentless 调用拿不到工作区上下文,守卫直接放行。
  • 子代理 / workflow / PTC 机制上走同一注册表,但未做端到端集成验证。
  • 其它插件直接调用 ctx.fs 且不触发 intent 的路径不在 fs-intent 层覆盖范围内;可通过 extraWriteTools / extraShellTools 扩展工具名。

非法配置会写 config.warning 到日志,而不是静默失效。

结尾

dsh-root-temp-guard 把仓库根目录临时项的约定落到 DSH 工具管线:参数守卫、写盘 intent、回合隔离清扫三层配合,配置驱动且随会话工作区根切换项目。若你的 profile 已由 CLI 管理,优先用官方 npm 安装命令接入 bundle。

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜