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
Introduction
dsh-steward · 对话管家
给 DeepSeek Harness 的对话管家: 每个对话一个文件夹、文件夹里自动写一份交接日志、可恢复的删除、 按文件类型分组的右键菜单、网址右键菜单, 外加一套由你手动开启的、写进全局
AGENTS.md的运行守则。
尚未发布到 npm。 本项目不提供 npm 包,上面的徽章里也没有 npm 版本号 —— 请用「安装」一节的 tarball 方式,
dsh plugin add dsh-steward解析不到任何东西。
🌏 中文 · English
平台支持
核心功能(对话文件夹、交接日志、删除对话、右键菜单、备份、体检)在 Windows / macOS / Linux 都能用。 但有几项是 Windows 专用,在别的系统上会直接隐藏,不会留一个点了没反应的按钮:
| 功能 | Windows | macOS / 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=<路径>]
删除对话(可恢复)
对话行「⋯」菜单 → 「删除对话」。
确认框里可勾选「同时删除该对话的文件夹」。确认后:
- 归档该对话
- 发
api-session/removed,让侧边栏立即移除该行 - 把会话记录目录移进回收站(不是删除)
- 清理归档记录
- 可选:把文件夹也移进回收站
内容进
$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 升级也不丢:
- 不破坏 DSH 与系统 —— 不碰安装目录 / profile 配置 / 会话凭据 / 系统设置;不为完成任务降级安全。
- 有风险的操作先验证 —— 说清「改什么 / 最坏怎样 / 怎么撤回」再执行;能小步试就不全量。
- 留好备份、保证可回退 —— 改前备份、确认回退路径存在、改完验证、验证不过就回退。
正文见 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-schema | schemastery 是否可用(拿不到就没设置表单) |
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.enabled | false | 是否每个新对话建一个文件夹 |
folder.trigger | first-message | first-message 或 session-start |
folder.template | chat-{date}-{time} | 支持 {date} {time} {title} {id} |
folder.useAsWorkingDirectory | false | 建好后是否把该对话的工作目录切过去 |
guardrails.install | false | 是否把三原则写进全局 AGENTS.md。默认关 |
sessionLog.enabled | true | 是否在对话文件夹里写 交接日志.md |
sessionLog.maxMessages | 400 | 最多导出多少条对话(0 = 不限,超出保留最近的) |
sessionLog.includeTools | true | 日志里是否包含工具调用与结果 |
设置页暴露
folder.enabled、sessionLog.enabled、guardrails.install三个开关; 其余字段需要手改这个 JSON。改完重启 DSH 生效。
卸载
设置 → 插件 → 已安装 → 移除。
$DSH_HOME/AGENTS.md 里的守则段不会自动删除(那是你的文件),
需要的话手删 <!-- dsh-steward:begin --> 与 <!-- dsh-steward:end --> 之间的内容。
node_modules/里指向 schemastery 的链接是设置表单用的, 删掉只会让设置页退化,不会让插件崩。