DeepSeek Harness 的会话管家:每个会话一个文件夹,删除可恢复,文件支持右键操作,三条安全基线写进全局 AGENTS.md。
31.4/100
综合分
2
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/11/3 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
相关生态插件(非 dsh 原生,按其对应运行时安装)
git clone https://github.com/John4874314/dsh-steward.git状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:已验证
是什么
生态应用
桌面端 / Web 外壳,不以 dsh plugin add 安装
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 2 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令读写/删除本地文件写入剪贴板(不计风险)发起外部网络请求
查看扫描证据(共 118 处,展示前 32 条)
- 读取本地文件index.js:215
entries = await readdir(root, { withFileTypes: true }) - 读取本地文件index.js:228
children = await readdir(direct, { withFileTypes: true }) - 读取本地文件index.js:250
const raw = await readFile(file, 'utf8') - 写入本地文件index.js:265
await writeFile(file, JSON.stringify(Object.fromEntries(map), null, 2), 'utf8') - 写入本地文件index.js:307
await rename(from, to) - 删除本地文件递归index.js:312
await rm(from, { recursive: true, force: true }) - 读取本地文件index.js:323
entries = await readdir(current, { withFileTypes: true }) - 读取本地文件index.js:335
const info = await stat(path) - 读取本地文件index.js:350
const raw = await readFile(join(dir, 'meta.json'), 'utf8') - 写入本地文件index.js:593
await mkdir(filesDir, { recursive: true }) - 写入本地文件index.js:617
await writeFile( - 写入本地文件index.js:638
await writeFile( - 写入本地文件index.js:647
await writeFile( - 执行外部命令来自配置/变量index.js:978
const child = spawn(command, [path], { detached: true, stdio: 'ignore', windowsHide: false }) - 执行外部命令来自配置/变量index.js:1002
const child = spawn(argv[0], argv[1], { detached: true, stdio: 'ignore', windowsHide: false }) - 执行外部命令来自配置/变量index.js:1020
const child = spawn(argv[0], argv[1], { detached: true, stdio: 'ignore', windowsHide: false }) - 删除本地文件递归index.js:1558
await rm(entryDir, { recursive: true, force: true }).catch(() => {}) - 删除本地文件递归index.js:1641
await rm(entryDir, { recursive: true, force: true }) - 删除本地文件递归index.js:1787
await rm(entryDir, { recursive: true, force: true }).catch(() => {}) - 删除本地文件递归index.js:1816
await rm(entryDir, { recursive: true, force: true }) - 删除本地文件递归index.js:1831
await rm(item.dir, { recursive: true, force: true }) - 执行外部命令来自配置/变量index.js:2701
execFile(file, args, { windowsHide: true, timeout: 8000 }, (error, stdout) => { - 执行外部命令来自配置/变量index.js:2832
const child = spawn(exe, [options.url], { detached: true, stdio: 'ignore', windowsHide: false }) - 执行外部命令固定命令index.js:2838
const child = spawn('cmd.exe', ['/c', 'start', '', options.url], { - 发起网络请求client.js:2249
const response = await fetch(src) - 发起网络请求client.js:2549
const blob = await (await fetch(menu.image.src)).blob() - 写入剪贴板client.js:2551
await navigator.clipboard.write([new ClipboardItem({ [type]: blob })]) - 写入剪贴板client.js:2563
await navigator.clipboard.writeText(menu.image.name) - 写入剪贴板client.js:2660
await navigator.clipboard.writeText(menu.path) - 写入剪贴板client.js:2698
await navigator.clipboard.writeText(value) - 写入剪贴板client.js:2866
await navigator.clipboard.writeText(menu.path) - 写入剪贴板client.js:2949
await navigator.clipboard.writeText(`@${menu.path}`)
扫描于 2026/11/4(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
依赖的 DSH / Cordis 模块
@deepseek-ai/schemastery@deepseek-ai/cordis给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
README
由 DeepSeek 最新模型翻译生成dsh-steward · 对话管家
给 DeepSeek Harness 的对话管家:每个对话一个文件夹、文件夹里自动写一份交接日志、可恢复的删除、按文件类型分组的右键菜单、网址右键菜单,外加一套由你手动开启的、写进全局 AGENTS.md 的运行守则。
尚未发布到 npm。 本项目不提供 npm 包,上面的徽章里也没有 npm 版本号 ——请用「安装」一节的 tarball 方式,dsh plugin add dsh-steward 解析不到任何东西。
🌏 中文 · English
平台支持
核心功能(对话文件夹、交接日志、删除对话、右键菜单、备份、体检)在 Windows / macOS / Linux 都能用。
展开完整文档(README 剩余 1 节)
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 链接。
- 想让智能体每次都遵守同一套安全基线,没有统一入口 —— 只能每个工作区各写一份 AGENTS.md。
这个插件把这五件事一次做完,并且只有一个 bundle、一个开关、一个设置页。
更新日志
1.6.3
存储页多了「按产生日期」分组,让你能判断这批孤儿是什么时候来的。
起因是个真问题:有人问「那些孤儿附件都是我删过的对话的产物吗」。答案是没法证明——附件按 sha256 内容寻址,文件名就是哈希,DSH 不记录「这张图来自哪个对话」。
唯一能用的线索是时间。所以现在扫完会按日期列出:
按产生日期(帮你判断这批是什么时候来的) 2026-10-25 1 个 0.0 MB 2026-10-21 27 个 67.1 MB ← 这一天的占了一大半,多半是同一次批量操作的产物 2026-10-20 6 个 17.7 MB 2026-09-30 16 个 2.6 MB
某一天占了总数一半以上时会额外标出来 —— 那通常是「某次批量粘贴」的特征,比零散分布更容易回忆起来。
日期按本地时区切,不是 UTC —— 你想的是「10 月 21 日那天」,不是 UTC 的那天。
新增 2 条测试(test/storage-scan.test.mjs 现共 9 条):按日期分组的个数与字节必须与孤儿总数严格一致(防止界面上出现自相矛盾的数字);没有孤儿时 days 是空数组。
1.6.2
修一个会误删还能用的附件的 bug:孤儿扫描没把回收站里的会话算进去。
scanAttachments 只扫 $DSH_HOME/sessions/,但 $DSH_HOME/trash/<id>/session/ 里还躺着被删对话的日志 —— 那些对话是可恢复的。只扫 sessions 会把它们引用的附件判成孤儿,用户一旦从回收站恢复那个对话,图片就没了。
「已删除」和「可恢复的已删除」是两件事,只有前者能让引用失效。
修复后在真实数据上的效果(同一台机器,修复前后对比):
| | 修复前 | 修复后 |
| 仍被引用 | 726 | 731 |
| 孤儿附件 | 55 | 50 |
| 孤儿占用 | 88.7 MB | 87.4 MB |
多出来的 5 个就是回收站里那个会话引用的附件 —— 之前会被误删。
界面上「仍被引用」那行现在会显示 + N 个回收站日志,让用户知道这个数是怎么来的。
新增两条测试(共 7 条):回收站会话引用的附件不算孤儿;活会话与回收站会话的引用要合并计算,不重复也不漏。
1.6.1
把「备份放哪、怎么写」变成随插件分发的约定,别人的智能体也能照做。
1.6.0 给备份加了来源标注,但那套东西只活在我这台机器的记忆里 —— 换个智能体、换台机器就不成立了。这次把它写进随插件分发的守则正文 (AGENTS.guardrails.md),于是只要你打开「运行三原则」, $DSH_HOME/AGENTS.md 里就会带上这段,智能体每次改文件前都会按它做备份。
新增的守则段落规定:
- 位置 $DSH_HOME/backups/<YYYYMMDD-HHmmss>-<是什么>-<在改什么之前>/,并明确「不要写死盘符」——别人机器上 DSH home 未必在 E 盘。
- 每条备份自带 meta.json,字段表(kind / label / source / workspace / reason / createdAt)逐条说明必填与否。
- 两条硬规矩:source 与 workspace 不许混用;不确定就不写,不按目录名猜来源。
同时纠正了 1.6.0 留下的一个语义错误:workspace 之前被我无条件填成同一个项目目录,对「与该项目无关的备份」来说这是错的信息,会让字段退化成噪音。
现在明确:与被备份内容无关时留空字符串。已按此修正两条历史备份(workbuddy 升级前备份、profile 锁文件改名前的备份)。
插件 README 也加了同样一节,说明这个目录不只插件在用、agent 的快照也放这儿。
新增 test 之外的端到端验证 12 条:守则注入幂等(连注两次结果完全一致)、不破坏用户原有内容、标记恰好一对、preview 与 get 正文一致。
1.6.0
备份分页:每条备份都说清自己是从哪来的。
以前「备份」分页只能看到一句「11 项 · 5.2 MB」,下面一堆 20261026-122749-steward-before-review-fixes 这样的目录名 —— 过两周连我自己都认不出哪个是哪个工作区的、为什么备份的。
这次给每条备份加一份 meta.json,界面按来源分组:
| 来源 | 什么时候产生 |
| 插件源码快照 | 改插件代码前的手动快照 |
| 插件升级前备份 | 升级其它插件(如 workbuddy)前的备份 |
| AGENTS.md 覆盖前自动备份 | 你点「恢复守则」时插件自动生成的 AGENTS.md.<时间戳>.bak |
| 手动备份 | 其它手动留的 |
| 来源未知(旧备份) | 没有 meta 的历史条目 |
三条刻意的取舍:
- 绝不根据目录名猜来源。 ...-steward-... 看着像插件快照,但名字里没有任何工作区或时间语义保证。猜出来的来源比没有来源更糟 —— 它看上去像真的。所以没有 meta 的一律进「来源未知」,界面上提供「补说明」入口让你自己填。
- 「来源未知」不允许整组清理。 那一堆恰恰是最需要逐个看一眼的,给个一键清掉的口子等于把风险最高的操作做得最省事。
- 按组清理也是移进回收站。 备份本身就是回退手段,删备份是最不该鲁棒性差的操作。
进了回收站还能捞回来。
顺带修了一个真 bug:/chat-backup 的子命令解析用的是整行 trim,于是 meta <id> 拼成 "meta 20261025-..." 永远不等于 'meta' ——所有带参数的子命令都被当成未知动作。已改成取第一个词。
命令行:
/chat-backup list # 按来源分组列出 /chat-backup meta <id> --kind=... --label64=... --workspace64=... --reason64=... /chat-backup trash <id...> # 指定条目 /chat-backup trash --group=<origin> # 按来源整组
1.5.0
新增「存储」分页:插件开始管它自己的垃圾了。
之前的清理功能(回收站、备份清空、孤儿文件夹)只管插件自己产生的东西。
真正占空间的三项——附件库、会话日志、备份——一直没有入口。这次补上第一项。
能看到的:附件库 $DSH_HOME/attachments/v1/objects 一共有多少文件、多大,多少还被会话引用,多少已经没有任何会话再引用它。
孤儿是怎么判定的:DSH 的附件按 sha256 内容寻址存放,文件名就是哈希。
插件把每个会话日志(多帧 zstd,追加式)逐帧解压成文本,抓出其中的 64 位 hex,与附件文件名求差集 —— 剩下的就是孤儿。
实测一台用了两个月的机器:775 个附件 / 335 MB,其中 75 个 / 152 MB 没有任何会话再引用。
三个刻意的取舍:
- 不自动扫描。切到这一页不会自动开跑,因为一次扫描要解压全部会话日志(实测约 1.5 秒)。先看轻量信息,点「扫描孤儿附件」才做重活。
- 不直接删。清理是把文件移进回收站,和删对话同一套逻辑,可以在「回收站」分页恢复。
恢复按 origin.json 记下的原始绝对路径逐个搬回,不是靠文件名倒推。
- 如实说明判定的边界。如果某个会话记录早已被删掉(删完对话又清空了回收站),它引用过的附件就会失去引用来源,从而被判成孤儿。这一点写在界面上,不在文档里藏着。
顺带修了一个真 bug:/chat-trash list 早先把「非 file 即 conversation」写死,导致新加的附件批次条目在回收站里显示成对话。现在按 file / attachments / conversation 三类正确区分。
新增 test/storage-scan.test.mjs(5 条):覆盖「被引用不算孤儿」「多帧必须逐帧解压」「没有日志时全部算孤儿」「目录不存在不抛错」「半截坏帧不影响判定」——第二条尤其重要,只读第一帧会把后面的引用全误判成孤儿。
命令行:/chat-storage scan | clean | open。
1.4.1
修:对话里 @ 引用的文件(截图里那个 交接日志.md 小标签)右键没有菜单。
那是 DSH 的「文件引用 chip」,DOM 上只有 title="交接日志.md"(文件名),完整路径只存在 React props 里,拿不到。所以:
- 客户端认出这个 chip(span[title] + 文本与 title 一致 + 内含 svg 图标,三条同时成立才算,避免误判普通带 title 的文字);
- 把文件名交给宿主,宿主在该会话的工作目录下按名查找;
- 同名文件有多份时,取层级最浅的那份(DSH 里 @x.md 本来就相对会话 cwd,浅层优先最接近原意),同时把「还有几份、分别在哪」一并告诉你,不静默挑一个;
- 搜索限深度 4 层、跳过 node_modules / .git / dist 等目录,最多 4000 条目。
菜单与正文路径一致:打开 / 在文件夹中显示 / 另存为 / 复制路径。
顺带修了一个识别 bug:点在 chip 内部的文字上时,之前会取到子节点(没有 title)而认不出来;现在统一用 closest 往上找。这个 bug 是新加的 test/chip-detect.test.mjs 抓到的。
1.4.0
新增:对话里的图片、正文里的文件路径,都能右键操作。
- 图片右键(.png / .jpg / .webp / .gif / .svg …):打开图片、在文件夹中显示、另存为、复制图片、复制文件名。图片在界面上是 blob: URL,没有磁盘路径,所以插件把图片字节算出 sha256 交给宿主,宿主按内容寻址去 $DSH_HOME/attachments/v1/ 找回原图 —— DSH 的附件本来就按 sha256 存,哈希就是主键。
(刻意不把图片字节塞进命令:命令会原样记进会话日志,80KB 的 base64 会把对话刷成乱码。)
- 正文路径右键:消息里出现的 E:\a\b.pdf、@docs/PRD.md、./notes.md 这类路径,右键就能「打开 / 在文件夹中显示 / 另存为 / 复制路径」。相对路径用该会话的工作目录补全,所以 @docs/PRD.md 这种写法也能直接打开。
- 打开/定位走宿主的系统调用,不是浏览器下载。没有后缀的附件对象会先物化成 $DSH_HOME/cache/dsh-steward/attachments/<哈希前缀>/<原文件名>,否则 Windows 不知道用什么程序打开它。
- 判定刻意保守:只有指针真的落在像路径的那一段文字上才弹菜单。
docs/PRD.md(没有 @)、https://…、普通句子一律不认 —— 宁可少弹,不要满屏乱弹。
- 新增 test/path-detect.test.mjs:把「什么算路径」的判定从源码里抠出来跑, 9 个正例 + 10 个反例,防止哪天手滑把正则放宽成"每句话都弹菜单"。
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> 换成你的配置档名(桌面端通常是 desktop,Web 端通常是 web)。
方式一:tarball(推荐,不依赖 git)
dsh plugin --profile <profile> add https://codeload.github.com/John4874314/dsh-steward/tar.gz/refs/tags/v1.6.3
不要求本机装 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 链接 | 复制成 链接文字 |
只接管 http / https。javascript:、data:、file: 一律放行给原生菜单,不进打开通道。
带账号密码的链接(https://user:pass@host/)不会被静默忽略 —— 那样右键毫无反应,像是坏了。
这类链接照常弹菜单,但:
- 顶部 URL 打码显示:https://user:***@host/,密码不露在屏幕上
- 多一行橙色警示:⚠ 这个网址包含账号密码
- 点「在浏览器打开」时弹一次确认框,说明凭据会被发给目标站点
- 复制类动作仍然可用,复制的是完整原始 URL(含真实密码)——复制到剪贴板是你主动的动作,不该被改写
安全靠「打开前确认」保证,不靠「什么都不给」。静默失败会让人以为插件坏了,明确警示才会让人真的看一眼地址。
图片右键菜单
对话里显示的图片(.png / .jpg / .jpeg / .webp / .gif / .svg / .bmp / .avif), 点右键就能:
| 动作 | 说明 |
| 打开图片 | 用系统默认看图程序打开 |
| 在文件夹中显示 | 在资源管理器里选中这个文件 |
| 另存为… | 选目录后复制一份(同名自动加序号) |
| 复制图片 | 图片直接进剪贴板,可以直接粘到聊天窗口 |
| 复制文件名 | 复制原始文件名(如 image.png) |
为什么需要绕一圈:界面里的图片是 blob: URL,浏览器不给你它的磁盘路径。
插件把图片字节算成 sha256,宿主按内容寻址到 $DSH_HOME/attachments/v1/ 找回原图。
DSH 的附件本来就按 sha256 存,哈希就是天然主键 —— 这样不必把 80KB 的图片 base64 塞进命令(那会把会话日志刷成乱码)。
正文里的文件路径也能右键
消息里出现的路径,右键就有「打开 / 在文件夹中显示 / 另存为 / 复制路径」:
E:\islet\docs\交接文档.md @docs/PRD.md ./notes.md ~/Documents/a.pdf
- 相对路径(@docs/PRD.md、./notes.md)用这个对话的工作目录补全,所以拖进去的引用可以直接打开。
- 只认「指针真的落在那段文字上」的情况,而且必须是完整的一段带路径特征的文本。
没有 @ 的 docs/PRD.md、https://…、普通句子都不会触发 —— 宁可少弹,不要满屏乱弹。
- 想省事的话,拖选一段路径再右键也行(选中优先)。
- 对话里带文件图标的 @引用 小标签(比如 交接日志.md)也能右键。那种标签的 DOM 上只有文件名、没有路径,所以插件会把文件名交给宿主,在这个对话的工作目录里按名查找;同名文件有多个时会列出全部候选让你看清,不会猜一个打开。
二、运行守则(三原则)—— 默认关闭,要你自己开
默认是关的。 安装本插件不会改动你的 $DSH_HOME/AGENTS.md。
想用三原则,去 设置 → 对话管家 → 运行守则 → 「启用运行三原则」自己打开。
打开后才会往全局文件里写内容;关掉后不再注入,已写的那段留在文件里,需要你手动删。
打开后把三原则写进 $DSH_HOME/AGENTS.md(DSH 的全局指令文件), 每个工作区、每个新会话都会带上,harness 升级也不丢:
1. 不破坏 DSH 与系统 —— 不碰安装目录 / profile 配置 / 会话凭据 / 系统设置;不为完成任务降级安全。
2. 有风险的操作先验证 —— 说清「改什么 / 最坏怎样 / 怎么撤回」再执行;能小步试就不全量。
3. 留好备份、保证可回退 —— 改前备份、确认回退路径存在、改完验证、验证不过就回退。
正文见 AGENTS.guardrails.md。
本 bundle 的补丁会重新启用 agent-instructions(web-app bundle 默认禁用了它),否则守则写进 AGENTS.md 也进不了上下文。副作用:项目里的 AGENTS.md / CLAUDE.md 也会被读进上下文。这一步与上面的开关无关 —— 它在 bundle 层,装了就有。
如果你只想要对话文件夹/右键菜单、完全不想让智能体读项目里的 AGENTS.md,请删掉 cordis.patch.yml 里 agent-instructions 那一段再安装。
三、设置页的五个分页
| 分页 | 内容 |
| 对话 | 每个新对话一个文件夹、交接日志 两个开关 |
| 回收站 | 列表 / 恢复 / 彻底删除 / 清空,显示每条占用 |
| 备份 | 备份目录位置与占用,一键清空 或 打开文件夹 自行取舍 |
| 运行守则 | 三原则开关、全文展开、写入结果预览、用内置版本恢复 |
| 体检 | 一次只读自检,报告守则块 / 表单 / 配置 / 登记表 / 回收站 / 备份 |
备份由你自己决定留还是删
写 AGENTS.md 等文件前会自动备份到 $DSH_HOME/backups/,备份会一直留着占磁盘。
「备份」分页因此给了两个按钮:一键清空 或 打开备份文件夹 自己挑。
插件不会替你自动删 —— 那是不可逆操作,该由你决定。
备份约定:每条都要说清自己从哪来
这个目录不只插件在用:你的智能体改东西前留的快照也放这儿。为了让这些备份在一段时间后还看得懂,插件把下面这套约定写进了随它分发的守则正文(AGENTS.guardrails.md)——你打开「运行三原则」后,它会随守则一起进 $DSH_HOME/AGENTS.md,于是智能体每次改文件前都会照它做。
位置:$DSH_HOME/backups/<YYYYMMDD-HHmmss>-<是什么>-<在改什么之前>/
不要写死盘符 —— DSH 的 home 在不同机器上位置不同。放在这里的额外好处是:它和 sessions/、trash/ 同一个卷,移动是 rename,不复制字节。
每条备份自带 meta.json(目录备份放在目录里):
{
"kind": "plugin-source",
"label": "dsh-steward 源码快照", "source": "/绝对路径/到/被备份的东西", "workspace": "/触发这次备份的工作目录,无关就留空", "reason": "加备份来源分组之前", "createdAt": 1793542028019 }
| 字段 | 必填 | 含义 |
| kind | 是 | plugin-source / plugin-upgrade / guardrails / manual |
| label | 是 | 一句话说清这是什么 |
| source | 是 | 被备份的东西原来在哪(不是触发者) |
| workspace | 否 | 哪次工作流触发的;与被备份内容无关时留空字符串 |
| reason | 是 | 为什么备份,用「<动作>之前」的句式 |
| createdAt | 是 | 毫秒时间戳 |
两条硬规矩:
1. source 与 workspace 不许混用。 备份的是 $DSH_HOME/profiles/... 下的插件,就不该把某个项目目录塞进 source;反过来,一次与工作区无关的备份, workspace 应当留空,而不是填一个「反正也对」的默认值 —— 那会让这个字段变成噪音。
2. 不确定就不写。 宁可 kind 留 manual、reason 空着,也不要根据目录名猜一个看起来合理的来源。猜出来的来源比没有来源更糟,因为它看上去像真的。
「备份」分页会读这些元信息并按来源分组(插件源码快照 / 插件升级前 / 守则自动备份 / 手动 / 来源未知)。没有 meta.json 的历史备份显示为「来源未知」,不会被猜;你可以直接在界面上给它补说明。「来源未知」那一组不提供整组清理 ——那堆恰恰最需要逐个看一眼。
体检报告什么
| 检查项 | 说明 |
| 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/,备份失败就放弃写入。
- 幂等:靠 定位,只替换自己那段;还会顺手清理旧 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_HOME/backups/ | 每次改 AGENTS.md 前自动备份 | 是,设置页可一键清空 |
| $DSH_HOME/trash/ | 删除对话 / 文件时移动进来 | 是,设置页可恢复 |
明确不会做的:
- 不联网,不上传任何东西。宿主侧没有任何 http / net / https 调用。
客户端有 2 处 fetch,只读本地 blob: / data: 地址(读界面里已经显示的那张图,用来算 sha256 和写剪贴板),不发往任何服务器。
- 不读 $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 里的守则段不会自动删除(那是你的文件),需要的话手删 与 之间的内容。
node_modules/ 里指向 schemastery 的链接是设置表单用的,删掉只会让设置页退化,不会让插件崩。
许可证
MIT预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
John4874314/dsh-steward
DeepSeek Harness 的会话管家:每个会话一个文件夹,删除可恢复,文件支持右键操作,三条安全基线写进全局 AGENTS.md。
相关插件 · 市场 / 管理
插件精选列表awesome-dsh-plugin/awesome-dsh-plugin
DeepSeek Harness(dsh)插件精选列表
★ 22,225未汉化DSH
73.6
新词雷达zhuyansen/new-term-radar
new-term-radar新词雷达
★ 2,049已汉化MCP
72.8
插件生态导航0xsline/awesome-deepseek-harness
安装 贡献指南 DeepSeek Docs 公开插件目录 Issues
★ 1,188已汉化DSH
72.6
技能注册中心iflytek/skillhub
企业级开源智能体技能注册中心——发布、发现并管理组织内可复用的技能包。
★ 5,370已汉化DSH
71.9
插件市场目录imsai-sh/awesome-deepseek-harness-plugins
面向 DeepSeek Harnessdsh生态的社区插件目录,共收录 13941 个插件含 PR 收录与…
★ 355已汉化DSH
71.8
插件精选清单Anil-matcha/awesome-dsh-plugin
DeepSeek Harnessdsh精选指南——DeepSeek…
★ 1,013已汉化DSH
71.7
这是你的仓库?认领条目