🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

对话管家 John4874314/dsh-steward

DeepSeek 客户端
John48743142 天前有提交已提供中文文档扫描:含敏感能力兼容 / 相关生态

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 安装
装得上吗
本站已在 dsh 0.2.1-rc.2 下真实安装成功
非静态推断
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 2 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
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
预览分享卡片长什么样 ↓John4874314/dsh-steward
分享 · DeepSeek Harness Hub
John4874314/dsh-steward

DeepSeek Harness 的会话管家:每个会话一个文件夹,删除可恢复,文件支持右键操作,三条安全基线写进全局 AGENTS.md。

这是你的仓库?认领条目

回到顶部