dsh-archive-browser
浏览 DeepSeek Harness (DSH) 中已归档的会话,并对其执行"再调用"。
背景:DSH 的「归档」是单向操作——官方只有
archiveSession,没有任何取消归档的入口, 且归档后的会话会被界面从所有列表里过滤掉(sessionVisible中的!archived.has(id))。 本插件补上了这块能力:把归档会话重新"捞"出来看、恢复、引用、续聊。
✨ 功能
侧边栏底部会出现一个 🗄 已归档 按钮,点开面板后,每个归档会话提供五项操作:
| 操作 | 说明 | 实现方式 |
|---|---|---|
| 查看内容 | 面板内渲染完整对话转录(用户 / 助手 / 工具调用) | host 读取并逐帧解码会话文件 |
| 影响面 | 只读体检:这个会话在你的电脑上动过哪些文件、跑过哪些命令、派生出了哪些会话 | host 解析 tool/call + tool/result |
| 恢复到侧边栏 | 从归档集合移除,会话重新出现在左侧列表(无需刷新) | 写 workspaceRegistry,触发官方增量广播 |
| 引入当前对话 | 把归档内容整理成 digest 写入当前输入框 | conversation 服务 → slash/input-insert-text 事件 → 剪贴板(三层降级) |
| 岔出继续对话 | 从归档会话岔出一个新的活会话并打开 | sessions.fork({ increaseTitle: true }) |
列表显示:标题、最后活动时间、工作区、消息数(用户/助手)、首条消息预览,以及岔出关系。
🔍 「影响面」是什么
点开每个归档会话的「影响面」,会得到一份只看不改的清单 —— 用来判断"这段对话到底在电脑上留下了什么":
| 区块 | 显示什么 |
|---|---|
| 产出 / 修改的文件 | 路径、新建还是修改、现在还在不在、大小、最后修改时间、被改过几次 |
| 执行过的命令 | 按 安装依赖 / 克隆仓库 / 下载 / 其它 分类高亮,附命令自带的描述与失败标记 |
| 派生出去的会话 | 小助手(子代理) 与 分支(fork) 分开标注 —— 分支在左侧列表里本来就看得见 |
每一步都有大白话解释,而且是根据每条的实际状态生成的(不是同一句套话)。
只读保证:不修改任何数据;文件只做"还在不在 / 多大 / 什么时候改的"三项检查,不读文件内容。
⚠️ 依赖不会被自动卸载。 面板只列出"这段对话装过什么、下载过什么",是否清理由你决定 —— 一个软件包可能别的会话或别的程序正在用,乱删会把其它东西弄坏。
数据来自哪里:转录里的工具调用参数是无损记录的(DSH 宁可抛错也不截断),所以
write/edit动过哪些文件、pwsh跑过哪些命令都能可靠重建;"新建 vs 修改"取自write结果里的Created file/Updated file标记。
📦 安装
方式一:从 GitHub 下载(现在就能用)
下载 ZIP:
codeload.github.com/.../zip/refs/heads/main(或在仓库页 Code → Download ZIP。注:部分网络下
github.com网页打不开,但上面这个直链可以。)解压后把顶层目录
dsh-archive-browser-main改名并放进:<DSH_HOME>\profiles\desktop\node_modules\dsh-archive-browser\<DSH_HOME>默认是C:\Users\<你的用户名>\.dsh。 放好后该目录里应该直接能看到package.json、lib\、cordis.patch.yml。在该 profile 的
cordis.patch.yml里追加:- insert: - id: archive-browser name: 'dsh-archive-browser'(这个文件默认内容可能只是一行
[]—— 如果有[],把它替换成上面的内容;如果已有其它条目,就追加到数组里。)重启 DSH Desktop。侧边栏底部就会出现 🗄 已归档。
第 4 步不能省:host 半的 HTTP 路由要在启动时挂载。不重启的话,点「影响面」会报
unknown archive api method "impact"。
方式二:官方 CLI(需要先发布到 npm)
dsh plugin --profile desktop add dsh-archive-browser
⚠️ 本包尚未发布到 npm(截至 2026-09-11,
registry.npmjs.org/dsh-archive-browser返回 404), 所以这条命令现在会报"找不到包"。发布到 npm 之后它会自动完成上面第 2~3 步。
🔧 改代码之后要不要重启?
- 改 client 半(
lib/client.js):被dsh-client-hmr自动热重载,不必重启、不必刷新。- 改 host 半(
lib/index.js/lib/archive-core.js):需要重启 DSH (实测:只改 host 文件不会自动重载,等 30 秒也不会)。- 首次安装后也要重启一次 —— host 半的 HTTP 路由要挂载,否则点「影响面」会报
unknown archive api method "impact"。
🧱 架构
lib/archive-core.js host 纯逻辑:定位会话文件、多帧 zstd 解码、元数据提取、转录
lib/index.js host 插件入口:Cordis apply + webServer 路由
lib/client.js client UI:极简 React 入口按钮 + 原生 DOM 面板
cordis.patch.yml 挂载声明(insert 行)
tests/ 离线测试(假 DOM / 假 req-res / 模拟 registry)
host 路由:POST /archive-browser/api/<method>
list / read / impact / restore / log(最后一个是诊断信道)
🔑 关键设计决策
1. 面板用原生 DOM,而不是 React 组件 + slot
这是踩坑后的结论。slots 实现里有一个「退位」机制:
// dsh-client-ui-slots
// 条目因崩溃上报而退位后,会在本次注册生命期内被排除出 entriesOfSlot 投影
abdicated = new WeakSet()
即 slot 条目的组件只要在渲染中抛一次异常,该注册就被永久退位,再也不会渲染—— 线上现象就是「点一下按钮就消失」。因此:
- 入口组件是极简 React 按钮(无 hooks / 无 Fragment / 无 Portal)
- 面板是原生 DOM,从点击处
appendChild(document.body),全程try/catch
附带好处:免疫 overflow:hidden 裁剪、外壳 transform 对 fixed 的干扰,
以及 shell.overlay 容器 pointer-events:none 的继承问题。
2. 恢复必须走 workspaceRegistry,绝不能直接改 workspace.json
WorkspaceRegistry 在内存里持有自己的 state,直改文件会被它下一次写入覆盖,
并触发启动时的一致性校验失败。正确做法是镜像 archiveSession 的写入协议:
registry.enqueueOperation(async () => {
const state = registry.requireState();
if (!state.archivedSessionIds.includes(id)) return;
await registry.setState({
...state,
archivedSessionIds: state.archivedSessionIds.filter((x) => x !== id),
});
});
写入后会广播 {type:'archived'} 增量,浏览器侧已经接好了这个通道,所以侧边栏会自动把该行放回来。
为什么客户端不能自己
open归档会话:uiWorkspace.clearArchivedCurrent()订阅了sessions.list, 会把任何选中归档会话的操作在下个微任务里回滚。必须先在 host 侧把 id 移出归档集合。
3. 会话文件是多帧 zstd,必须逐帧解码
会话存于 <DSH_HOME>/sessions/<cwd 编码>/<sessionId>/session.jsonl.zstd,
是多个 zstd 帧拼接的容器。Node 的 zstdDecompressSync 与流式解压只解第一帧,
所以 archive-core.js 复刻了 DSH 官方的 scanZstdFrames 结构扫描算法逐帧解码。
元数据来源(JSONL 记录):
| 字段 | 来源 |
|---|---|
| 标题 | session/title(取最后一条) |
| 首条消息 | 第一条 user/message |
| 工作区 / 预设 | 头记录 session |
| 时间 | 记录 time |
| 消息数 | 统计 user/message + assistant/message |
| 岔出关系 | 头记录 parentSession |
4. 面板不透明化(对抗半透明主题)
wallpaper-engine 之类的插件会让主题背景变半透明,导致面板可读性变差。
本插件不直接使用主题色板,而是自行解析出一套不透明配色:
从框架文本色判断深/浅色,仅在框架背景已完全不透明时才复用它,否则回退到实色;
所有文字颜色均为显式值(不在半透明表面上叠 opacity),遮罩加深并加模糊。
🧪 测试
node tests/host_harness.js # host HTTP 层(假 req/res + 模拟 registry)
node tests/client_harness.js # 客户端交互(假 DOM + 假 fetch,真实执行 lib/client.js)
两个测试都不触碰真实归档数据:restore 的写入发生在模拟 registry 上。
⚠️ 已知限制
- 岔出需要"已完成的回合":没有任何助手消息的会话会报
session/fork-unavailable(DSH 自身限制)。 - "引入当前对话"是尽力而为:会话作用域事件未必跨作用域到达,故实现了三层降级,最差退化为复制到剪贴板。
workspaceRegistry的setState/requireState/enqueueOperation是 TS 私有但运行时公开的内部接口 (因为没有公开的取消归档 API)。升级 DSH 版本后建议回归验证。- host 路由受 DSH 的令牌/来源守卫保护,跨源访问会被拒绝(属预期行为)。
📄 License
MIT
No comments yet. Be the first to write one.