dsh-session-repair
已验证@flashyiyi/dsh-session-repair · v0.2.0 · MIT
DSH session log repair: scans zstd session logs for event types the host does not know and marks them ignorable with a compliant frame rewrite (backup included). Skips archived sessions by default, caches per-file verdicts, and is read-only in scan mode.
安装
dsh plugin add @flashyiyi/dsh-session-repair 用 dsh --profile default --dump-config 确认 layer 已生效 —— 参见安装指南。
源码
发布到 npm 但没有公开仓库。安装前请检查包内容。
标签
说明文档
dsh-session-repair
DeepSeek Harness 插件:修复因未知事件类型导致无法打开的会话日志。
背景
dsh 0.1.0-rc.x 没有第三方事件类型注册机制。外置插件向会话日志写入自定义事件(如 message-edit/version)后,服务重启恢复该会话时抛出 SessionFormatUnsupportedError。官方读端支持 ignorable: true 标记,本插件即为这类事件补齐标记并按合规帧格式重写日志。
用法
- 工具:
session_repair - 参数:
action:scan只报告 /repair修全部非 live、非归档会话 /repair_id修指定会话sessionId:repair_id的目标会话verbose:是否返回逐文件明细(默认 false,只回汇总 + 需修文件)
- 返回:
scanned(本轮真正解压分析的文件数)、cached(命中增量缓存未解压的)、skippedArchived/skippedLive、unknownTypes(按类型聚合的未知事件)、fixable(scan)或repaired(repair)
示例(scan 输出):
{ "action": "scan", "files": 299, "scanned": 47, "cached": 252, "skippedArchived": 252,
"unknownTypes": { "computer-use/observed": { "count": 1, "ignorable": 1, "files": 1 } },
"fixable": [] }
事件判据(v0.2.0 修正)
判定「未知事件」用的是两份清单的并集:
- 宿主导出的
KNOWN_SESSION_EVENT_TYPES(@deepseek-ai/dsh-session,即当前 build 的SessionEventMap成员); - 已发布的历史格式版本事件类型(
assistant/chunk、text-chunks、reasoning-chunks、tool-call-chunks、tool/code-dispatch、tool/code-dispatch-start)——它们不在当前SessionEventMap里,但宿主读旧日志时会经 format 迁移链(v0→v1→v2→v3)正常识别。
只用手抄清单会漏掉后来新增的内置类型(system/message、assistant/attempt、model/selection 等),只用 KNOWN_SESSION_EVENT_TYPES 又会把旧格式的流式事件整片误判为未知。两份都不是全集,因此取并集。
这个并集是可证明完整的,不是经验拼凑:v0 已发布清单(51 个类型)里不被当前宿主认识的恰好只有 assistant/chunk、tool/code-dispatch、tool/code-dispatch-start 三个(断言见 session-format-v0-to-v1/tests/validation.spec.ts:218),v2 清单 ⊆ v0 ∪ {assistant/attempt}(session-format-v1-to-v2/src/dispositions.ts:7-29),再补 3 个 codec 打包信封标签(PACKED_TAGS:text-chunks / reasoning-chunks / tool-call-chunks)即覆盖 v0/v1/v2/v3 全代。
不要改成"读文件名判版本"或"在压缩数据里搜魔数":文件名不代表格式版本(迁移只升级逻辑 header、从不重命名物理文件,所以 session.jsonl.zstd 里完全可能是 version 3 的 header),压缩数据里也可能偶然出现 4 字节魔数序列。两者都会造成大批误报。
限制:v0 会话修不了(本工具会如实拒绝)
ignorable 是 v1+ 的兼容机制。v0→v1 迁移层对未知历史事件是"refuses unknown historical events even when ignorable",所以给 v0 日志里的未知事件补 ignorable 完全无效。
v0.2.0 起按 header 的 version 分流:version 0 的文件不会被动一个字节,其未知事件进 unfixableV0 数组,附带原因。修 v0 需要把事件替换成 payload 为空的合法类型({"type":"session/end-seed","seq":N,"time":T,"data":{}},seq 必须保持稠密),这是另一个工具:
.agents/skills/dsh-session-cleanup/scripts/fix-session-frames.mjs --rule=replace-type --types="computer-use/*" --all
(v0.2.0 之前的行为是:照样重写文件、报 repaired,而会话依然打不开——静默空转,已修。)
行为
- 默认跳过已归档会话:归档只往
archivedSessionIds记 id,会话原文仍留在sessions/目录里;全量分析它们纯属开销。repair_id显式点名时放行,保证归档会话损坏后仍有修复入口。 - 只重压改动过的帧:写回时逐帧比对,没有改动行的帧原字节保留。修复开销因此约等于改动体量——实测给一个 64 KB / 22 帧的会话补 2 条
ignorable,文件只涨 11 字节(0.02%),且其余 21 帧与修复前逐字节一致。v0.2.0 之前把每一行压成独立帧,同样场景会让文件膨胀约 30%(1189 KB → 1553 KB)。 - 增量缓存:按
size + mtime记住每个文件的结论,未变化的文件不再解压;宿主升级导致事件词汇表变化时缓存整体失效(缓存文件~/.dsh/dsh-session-repair/scan-cache.json)。 - 逐帧同步解码 + 周期性让出:Node 的 zstd 一次调用只解首帧(多帧容器必须逐帧解),同步解码比逐帧
await快约 3 倍,每 8 ms 让出一次事件循环避免冻结宿主。
实测(299 个日志文件 / 578.8 MB,其中 252 个已归档被跳过):首次全量 31.5 s,再次扫描 0.24 s;改造前同样规模需要 232.6 s 且会超出自带的 60 s 超时。
可靠性
scan默认零改动;repair只处理非 live、非归档会话;- 修复前强制备份(
.before-ignorable),幂等(已标记事件不重复处理);version 0 的文件永不写盘(见上文限制),撕裂尾帧的文件也拒绝重写; - 帧定位走 zstd 帧结构(frame header descriptor + block header 链 + 可选校验和),不在压缩数据里搜魔数——后者是概率性正确,一旦误命中就把一帧劈成两半;
- 写回严格保格式:首帧永远是 header 且逐字节不变(旧版曾给 header 注入
ignorable,读端会判released v0 physical header has unexpected member "ignorable",把会话修成彻底打不开),其余帧除改动行外原字节保留,每个重压帧写前做decompress(compress(x)) === x往返校验,写回后复查帧数与撕裂状态; - 解压失败 / 首行非法 / 扫描期间文件被宿主重写而读不到,都只跳过该文件并报告,不强行写回;
- 零运行时依赖、无自定义会话事件、无 HTTP 路由。
安装
dsh plugin --profile web add @flashyiyi/dsh-session-repair
# 重启 dsh web
来源
Fork 自 Equinox7379/dsh-session-repair(MIT,Copyright (c) 2026 Equinox7379)。 v0.2.0 起由 flashyiyi 维护,改动:
- 事件判据取「宿主已知类型 ∪ 已发布格式版本历史类型」的并集(并集完整性有源码断言支撑,见上文);
- 默认跳过已归档会话,
repair_id仍可点名; - 按
size + mtime增量缓存,scan默认只回汇总; - 逐帧同步解码 + 每 8 ms 让出事件循环;
- 帧定位改走 zstd 帧结构,不再在压缩数据里搜魔数;
- 重写只重压含改动行的帧(旧版逐行压帧,文件膨胀约 30%);
- 按 header
version分流:v0 的未知事件补ignorable无效,改为如实报告unfixableV0且不写盘(旧版会假报repaired)。
License
MIT(见 LICENSE,保留原始版权声明)