← Back to home@John4874314

dsh-steward

Conversation steward for DeepSeek Harness: one folder per conversation, recoverable delete, right-click file actions, and a three-rule safety baseline written into the global AGENTS.md.

Stars
1
Language
JavaScript
Created
Oct 24, 2026
Updated
Oct 26, 2026
GitHub repo

Introduction

dsh-steward · 对话管家

license CI DSH

给 DeepSeek Harness 的对话管家: 每个对话一个文件夹、文件夹里自动写一份交接日志、可恢复的删除、 按文件类型分组的右键菜单、网址右键菜单, 外加一套由你手动开启的、写进全局 AGENTS.md 的运行守则。

尚未发布到 npm。 本项目不提供 npm 包,上面的徽章里也没有 npm 版本号 —— 请用「安装」一节的 tarball 方式,dsh plugin add dsh-steward 解析不到任何东西。

🌏 中文 · English


平台支持

核心功能(对话文件夹、交接日志、删除对话、右键菜单、备份、体检)在 Windows / macOS / Linux 都能用。 但有几项是 Windows 专用,在别的系统上会直接隐藏,不会留一个点了没反应的按钮:

功能WindowsmacOS / Linux
每个对话一个文件夹、交接日志✅✅
删除对话(可恢复)、批量删除✅✅
文件右键菜单:打开 / 复制路径 / 另存为 / 重命名 / 移到回收站✅✅
文件右键菜单:解压到此处✅ 用系统 tar.exe❌ 隐藏
网址菜单:用其它浏览器打开✅ 读 StartMenuInternet 注册表❌ 隐藏(只留「在默认浏览器打开」)
文件右键菜单:在终端中打开✅❌ 隐藏

如果你在非 Windows 上发现某个菜单项显示了但点了没反应,那是 bug,请开 issue。

它解决什么问题

DSH 原生把「对话」放在侧边栏,但:

  • 对话一多,找不到某个对话对应的文件 —— 它没有「每个对话一个目录」的概念。
  • 删对话是不可逆的 —— 手滑一次,记录就没了。
  • 对话里的文件卡片只能点开,不能右键做「打开文件夹 / 复制路径 / 另存为」; 而且不同文件想做不同的事(图片、代码、压缩包),没有分类型的入口。
  • 对话里的链接只能点开 —— 不能换浏览器打开、不能只复制 URL、不能复制成 Markdown 链接。
  • 想让 agent 每次都遵守同一套安全基线,没有统一入口 —— 只能每个工作区各写一份 AGENTS.md。

这个插件把这五件事一次做完,并且只有一个 bundle、一个开关、一个设置页。

更新日志

1.3.1

修 1.3.0 引入的一个回归:本地包名残留 @local/。

1.3.0 把 patch 与 client 的 id 统一改成了 dsh-steward,但漏了本地开发目录的 package.json,它的 name 还是 @local/dsh-steward。

DSH 的 assertPackageOwned() 会从 icon.svg 的真实路径向上找一个 name 等于包名的 package.json。先找到的那个是 @local/dsh-steward,对不上,于是整个插件被标记为:

包元信息错误:Plugin metadata for dsh-steward:
icon.svg: icon must remain inside its package directory

界面上表现为插件被自动移出 bundle 列表并显示「已关闭」。

  • 本地 package.json 的 name 改为 dsh-steward,与发布版完全一致。 本地和发布从此共用同一个包身份,这类「本地能跑、发布坏 / 发布修了、本地又坏」的分叉不会再出现。
  • exports 补上 "./package.json": "./package.json"(其它插件都有;缺了会让 <包名>/package.json 解析报 ERR_PACKAGE_PATH_NOT_EXPORTED)。
  • 自检从 6 条扩到 9 条:新增「exports 必须导出 ./package.json」「包名必须是裸名, 不带 @scope/」「icon 满足 DSH 的类型/大小/包内约束」。已验证把 name 改回 @local/ 会立刻让 2 条断言变红。

1.3.0

这一版修的是一个发布阻断级 bug:1.0.0 ~ 1.2.2 从 tarball / npm 安装都加载不了。

  • 修 cordis.patch.yml 的 name:写的是本地开发用的 @local/dsh-steward, 而真实包名是 dsh-steward。宿主按 name 解析模块,所以装完会直接 ERR_MODULE_NOT_FOUND: Cannot find package '@local/dsh-steward', 插件整块不激活(dsh: warning: 1 entry did not activate)。 本地开发时看不出来,因为本地恰好装成了 @local/ 作用域 —— 这是它能溜进发布版的原因。
  • client.js 的 module id 同步改为 dsh-steward,与包名一致。
  • 运行守则改为默认关闭。以前 guardrails.install: true 且代码里用 !== false 判断,undefined 会被当成开启 —— 装个插件就往全局 AGENTS.md 写东西。 现在只有显式打开才写。要三原则请去设置页自己开。
  • 补上最小自检与 CI:npm test 跑断言,其中第一条就是 「package.json / patch insert / client module id 三个名字必须一致」。
  • README 去掉 npm 徽章(包没发布,徽章是假的),并补上平台支持矩阵。

1.2.0

  • 每个对话的文件夹里自动写一份「交接日志.md」:概览(会话 id / 工作目录 / 起止时间 / 消息数)、本次产出的文件清单、按时间顺序的对话记录(用户说了什么、助手回了什么、 调了哪些工具及成败)。用来把任务交给下一个人,或几天后的自己。
  • 自动触发:建目录时一次,之后每个轮次结束一次(同会话 4 秒节流); 整份重新生成后原子覆盖,不会留半截文档。
  • 剔除 DSH 每轮注入的 <system-reminder>(实测占原始事件篇幅 48%), 超长消息保留头尾并标注省略字数,默认保留最近 400 条对话。
  • 新增命令 /chat-session-log --session=<id>,给历史对话补导一份。
  • 设置页「对话管理」里新增「在文件夹里写「交接日志.md」」开关(默认开启, 仅在文件夹开关打开时显示)。

1.1.0

  • 文件右键菜单按类型分组:代码 / 文档 / 压缩包 / 图片 / 音视频 / 可执行 / 其它, 每类给一套专属动作(复制代码、解压到此处、复制文件内容、文件信息…)。
  • 网址右键菜单:在默认浏览器打开、用其它浏览器打开、复制网址、复制为 Markdown 链接。
  • 带账号密码的链接不再被静默忽略:菜单照常弹出,密码打码显示,打开前弹确认框。
  • 右键范围从「交付物卡片 + 变更审查行」扩展到右侧文件树。

1.0.0

首个发布版:对话文件夹、可恢复删除、批量删除、文件右键菜单、运行守则、备份管理、体检。

安装

把 <profile> 换成你的 profile 名(桌面端通常是 desktop,Web 端通常是 web)。

方式一:tarball(推荐,不依赖 git)

dsh plugin --profile <profile> add https://codeload.github.com/John4874314/dsh-steward/tar.gz/refs/tags/v1.3.1

不要求本机装 git,也不要求 github.com:443 可直连 —— 只走 codeload.github.com。

方式二:github: 简写

dsh plugin --profile <profile> add github:John4874314/dsh-steward

需要本机 PATH 里有 git,且能连上 github.com:443。在国内网络下 github.com 常常超时(api.github.com / codeload.github.com 却正常), 这时会报 ERR_PNPM_GIT_RESOLVE_FAILED,换方式一即可。

装完之后

重启 DSH。客户端 bundle 图在进程启动时构建,只按 Ctrl/Cmd + Shift + R 刷新页面 不会重新扫描新插件。

环境要求

  • DSH 0.2.1-rc.1 或更新(在 0.2.1-rc.3 上实测通过)
  • Node ≥ 20
  • Windows / macOS / Linux 均可装;其中解压、多浏览器选择、终端三项目前是 Windows 专用, 见上面的「平台支持」。

装完先自检

不信任一个第三方插件是合理的。装完可以自己跑一遍它的自检:

git clone https://github.com/John4874314/dsh-steward && cd dsh-steward
npm test          # 9 条断言,不需要装任何依赖

或者只跑插件自带的只读体检:设置 → 对话管家 → 体检。它只读,不改任何文件。

功能

一、对话管理

每个新对话一个文件夹(默认关闭)

设置 → 对话管家 → 对话管理 → 「每个新对话创建一个文件夹」。

  • 默认在发出第一条消息时才建,空会话不留目录。
  • 子代理不建(跟随父会话);路径越界拒绝;非法字符替换成 -。
  • 开关即时生效,配置存 $DSH_HOME/chat-manager-config.json。

文件夹里自动写「交接日志.md」(默认开启)

设置 → 对话管家 → 对话管理 → 「在文件夹里写「交接日志.md」」。 这个开关只在「每个新对话创建一个文件夹」打开时才出现 —— 没有文件夹就没有落脚点。

每个对话自己的文件夹里会多出一份 交接日志.md,用途是把任务交给别人(或几天后的自己):

段落内容
概览会话 id、标题、工作目录、创建时间、导出时间、消息数、省略了多少
产出的文件这次对话里 present 过的交付物,一行一个绝对路径 + 说明
对话记录按时间顺序:用户说了什么、助手回了什么、调了哪些工具(成功/失败)

几个刻意的取舍:

  • 什么时候写:建目录时写一次;之后每个 turn/end 与 session/end-seed 各写一次, 同会话 4 秒内只写一次(节流),避免长对话把磁盘写满。
  • 怎么写的:整份重新生成、写 .tmp 再 rename 原子覆盖 —— 不会留下半截文档。
  • 为什么不是原样复制:DSH 每轮都会把 <system-reminder>(内含 AGENTS.md 全文) 拼进用户消息。实测一份 1.2 万条事件的对话里这类内容占 48%、同一份守则重复上千遍。 日志会剔除这些注入块,只留人真正说过的话。
  • 单条消息过长会截断:默认每条上限 4000 字,保留头尾并标注省略了多少字; 对话条数上限默认 400(超出的较早消息整段省略,页首写明)。 完整原文始终在 $DSH_HOME/sessions/ 里,日志只是"能读的摘要"。

想给一个历史对话补一份日志(早于本功能、或当时开关是关的):

/chat-session-log --session=<会话 id> [--force] [--folder=<路径>]

删除对话(可恢复)

对话行「⋯」菜单 → 「删除对话」。

确认框里可勾选「同时删除该对话的文件夹」。确认后:

  1. 归档该对话
  2. 发 api-session/removed,让侧边栏立即移除该行
  3. 把会话记录目录移进回收站(不是删除)
  4. 清理归档记录
  5. 可选:把文件夹也移进回收站

内容进 $DSH_HOME/trash/<id>/,并写一条 meta.json 记下标题、时间、原路径。 设置页「回收站」里可以恢复或彻底删除。

打开该对话的文件夹

对话行「⋯」菜单 → 「打开该对话的文件夹」,直接在资源管理器里定位。 没有登记过文件夹的对话不显示这一行 —— 不占位,也不会出现点了没反应的按钮。

批量删除

/chat-delete-batch --sessions=<id1,id2,...> [--with-folder]

逐个走同一条删除路径,全部进回收站,返回每个的成功/失败明细。

文件右键菜单(按类型分组)

对话里的文件卡片、右侧「变更」审查行、右侧文件树,点右键(或用键盘 Shift+F10 / 菜单键)都能弹出菜单。

菜单会先认文件类型,再决定放哪些动作。通用项永远在,专属项插在中间:

类型识别方式专属动作
代码.js .ts .py .go .rs .java .c .cpp .cs .php .sh .ps1 .sql .html .css .json .yaml …复制代码、复制为 Markdown 代码块(带语言标注)
文档.md .txt .pdf .docx .xlsx .pptx .csv .epub …复制文件内容
压缩包.zip .7z .rar .tar .gz .tgz .bz2 .xz .zst …解压到此处(同名目录已存在则拒绝,不覆盖)
图片.png .jpg .gif .webp .svg .bmp .ico .avif …文件信息
音视频.mp4 .mkv .mov .mp3 .wav .flac .m4a …文件信息
可执行.exe .msi .dll .so .dylib .bin .jar .wasm …只给「文件信息」,不提供任何运行入口
其它其余扩展名文件信息

通用项:

动作说明
打开 / 打开文件夹 / 选择打开方式走 remote.session.*,不写会话日志
在终端中打开在该文件所在目录起一个终端窗口
复制路径复制绝对路径
复制为引用复制成 @<路径>,粘贴到输入框即可定位
另存为…选目录后由宿主复制一份(同名自动加序号)
重命名…同目录内改名,不覆盖已有文件
文件信息大小 + 修改时间
移到回收站文件进 $DSH_HOME/trash/,可恢复

外观是液态玻璃,颜色取 --dsw-alias-* 令牌,随深浅主题自动切换。菜单顶部的类型标签按类别着色(代码蓝、压缩包橙、可执行红),扫一眼就知道这是哪类文件。

为什么不给可执行文件「运行」:右键菜单的路径来自对话内容,是模型可以影响的输入。 一个「运行」按钮等于给了一个静默执行任意程序的入口,收益远小于风险。

「打开 / 打开文件夹 / 打开方式 / 复制路径 / 复制为引用」不写日志; 「在终端中打开 / 另存为 / 重命名 / 移到回收站 / 解压 / 复制内容」必须由宿主执行,走 /chat-file, 会在对话里留一条命令记录 —— 这是浏览器无法直接写本地文件的必然代价。

网址右键菜单

对话里的 http/https 链接上点右键,得到一套网址专用动作:

动作说明
在默认浏览器打开交给系统默认浏览器
用其它浏览器打开列出本机已注册的浏览器(读 StartMenuInternet),逐个可选
复制网址复制原始完整 URL
复制为 Markdown 链接复制成 [链接文字](URL)

只接管 http / https。javascript:、data:、file: 一律放行给原生菜单,不进打开通道。

带账号密码的链接(https://user:pass@host/)不会被静默忽略 —— 那样右键毫无反应,像是坏了。 这类链接照常弹菜单,但:

  • 顶部 URL 打码显示:https://user:***@host/,密码不露在屏幕上
  • 多一行橙色警示:⚠ 这个网址包含账号密码
  • 点「在浏览器打开」时弹一次确认框,说明凭据会被发给目标站点
  • 复制类动作仍然可用,复制的是完整原始 URL(含真实密码)——复制到剪贴板是你主动的动作,不该被改写

安全靠「打开前确认」保证,不靠「什么都不给」。静默失败会让人以为插件坏了, 明确警示才会让人真的看一眼地址。

二、运行守则(三原则)—— 默认关闭,要你自己开

默认是关的。 安装本插件不会改动你的 $DSH_HOME/AGENTS.md。 想用三原则,去 设置 → 对话管家 → 运行守则 → 「启用运行三原则」自己打开。 打开后才会往全局文件里写内容;关掉后不再注入,已写的那段留在文件里,需要你手动删。

打开后把三原则写进 $DSH_HOME/AGENTS.md(DSH 的全局指令文件), 每个工作区、每个新会话都会带上,harness 升级也不丢:

  1. 不破坏 DSH 与系统 —— 不碰安装目录 / profile 配置 / 会话凭据 / 系统设置;不为完成任务降级安全。
  2. 有风险的操作先验证 —— 说清「改什么 / 最坏怎样 / 怎么撤回」再执行;能小步试就不全量。
  3. 留好备份、保证可回退 —— 改前备份、确认回退路径存在、改完验证、验证不过就回退。

正文见 AGENTS.guardrails.md。

本 bundle 的 patch 会重新启用 agent-instructions(web-app bundle 默认禁用了它), 否则守则写进 AGENTS.md 也进不了上下文。副作用:项目里的 AGENTS.md / CLAUDE.md 也会被读进上下文。这一步与上面的开关无关 —— 它是 bundle 层的,装了就有。 如果你只想要对话文件夹/右键菜单、完全不想让 agent 读项目里的 AGENTS.md, 请删掉 cordis.patch.yml 里 agent-instructions 那一段再安装。

三、设置页的五个分页

分页内容
对话每个新对话一个文件夹、交接日志 两个开关
回收站列表 / 恢复 / 彻底删除 / 清空,显示每条占用
备份备份目录位置与占用,一键清空 或 打开文件夹 自行取舍
运行守则三原则开关、全文展开、写入结果预览、用内置版本恢复
体检一次只读自检,报告守则块 / 表单 / 配置 / 登记表 / 回收站 / 备份

备份由你自己决定留还是删

写 AGENTS.md 等文件前会自动备份到 $DSH_HOME/backups/,备份会一直留着占磁盘。 「备份」分页因此给了两个按钮:一键清空 或 打开备份文件夹 自己挑。 插件不会替你自动删 —— 那是不可逆操作,该由你决定。

体检报告什么

检查项说明
guardrails守则块在不在、标记是否配对、有没有残留旧标记
config-schemaschemastery 是否可用(拿不到就没设置表单)
config-file配置文件能否读取、当前开关值
folder-registry登记表里有多少条指向已不存在的目录
trash回收站条目数与占用
backups备份条目数与占用

全部只读,不改任何文件。

为什么不会拖垮 harness

这是本插件最看重的设计目标 —— 任何失败都只表现为「少一个功能」,绝不连累 DSH 本身:

  • 宿主半侧没有 Config 就降级:schemastery 用运行时尝试获取(先本地、再 profile), 拿不到就导出 Config = undefined,插件照常加载,只是没有设置表单。
  • 守则写入整段包在 try/catch,apply 结尾 catch(() => {}),任何失败都只是「没写进去」。
  • 写 AGENTS.md 前先备份到 $DSH_HOME/backups/,备份失败就放弃写入。
  • 幂等:靠 <!-- dsh-steward:begin/end --> 定位,只替换自己那段; 还会顺手清理旧 guardrails 插件留下的 guardrails:begin/end 段。
  • 客户端适配是弱引用:对其它插件(如 dsh-better-sidebar)的集成一律用 ctx.get(...) 探测 + try/catch,目标不存在就跳过,不影响本体。

已做故障注入:DSH_HOME 不存在、config 为 null、依赖缺失、重复 apply 均不抛错。

关于信任:这个插件会碰什么、不会碰什么

一个第三方插件要能删对话、能写文件,你当然该先问清楚它到底动什么。

会写的文件(全都在你明确开启对应开关之后):

路径什么时候能不能反悔
$DSH_HOME/chat-manager-config.json你在设置页改开关是,就是个 JSON
对话文件夹 + 其中的 交接日志.md打开「每个新对话一个文件夹」后是,删掉即可
$DSH_HOME/AGENTS.md 里 <!-- dsh-steward:begin/end --> 之间你手动打开「运行三原则」是,关掉后手动删那一段
$DSH_HOME/backups/每次改 AGENTS.md 前自动备份是,设置页可一键清空
$DSH_HOME/trash/删除对话 / 文件时移动进来是,设置页可恢复

明确不会做的:

  • 不联网。整个插件没有任何 fetch / http / net 调用,不上传任何东西。
  • 不读 $DSH_HOME/sessions/ 之外的数据;读会话只是为了生成交接日志,走 DSH 官方 sessionController.inspect() 通道,不直接解析磁盘。
  • 不改 DSH 安装目录、不改 cordis.yml / cordis.patch.yml、不改系统设置。
  • 删除一律走回收站,不直接 rm;清空回收站需要你二次确认。

已知的粗糙之处(写在这里而不是藏起来):

  • 单文件插件,index.js 约 2,600 行、client.js 约 2,400 行,没有拆分模块。
  • 测试只有 9 条契约断言,覆盖「名字一致 / 默认值 / 模块能加载 / icon 元信息」, 不覆盖 UI 交互。
  • Windows 优先开发,非 Windows 的三项功能是直接隐藏而非降级实现。
  • 尚未发布 npm,只能从 git / tarball 装。

配置

配置文件在 $DSH_HOME/chat-manager-config.json:

{
  "folder": {
    "enabled": false,
    "trigger": "first-message",
    "template": "chat-{date}-{time}",
    "useAsWorkingDirectory": false
  },
  "guardrails": {
    "install": false
  },
  "sessionLog": {
    "enabled": true,
    "maxMessages": 400,
    "includeTools": true
  }
}
字段默认说明
folder.enabledfalse是否每个新对话建一个文件夹
folder.triggerfirst-messagefirst-message 或 session-start
folder.templatechat-{date}-{time}支持 {date} {time} {title} {id}
folder.useAsWorkingDirectoryfalse建好后是否把该对话的工作目录切过去
guardrails.installfalse是否把三原则写进全局 AGENTS.md。默认关
sessionLog.enabledtrue是否在对话文件夹里写 交接日志.md
sessionLog.maxMessages400最多导出多少条对话(0 = 不限,超出保留最近的)
sessionLog.includeToolstrue日志里是否包含工具调用与结果

设置页暴露 folder.enabled、sessionLog.enabled、guardrails.install 三个开关; 其余字段需要手改这个 JSON。改完重启 DSH 生效。

卸载

设置 → 插件 → 已安装 → 移除。

$DSH_HOME/AGENTS.md 里的守则段不会自动删除(那是你的文件), 需要的话手删 <!-- dsh-steward:begin --> 与 <!-- dsh-steward:end --> 之间的内容。

node_modules/ 里指向 schemastery 的链接是设置表单用的, 删掉只会让设置页退化,不会让插件崩。

许可证

MIT