Skip to content

dsh-session-mover

Verified

@zhengjunyao/dsh-session-mover · v0.1.1 · MIT · Web UI

Move a DSH conversation between workspaces: relocates the session directory, rewrites the stored cwd, and moves the workspace membership — back up first, roll back on failure, preview before you commit

Install

dsh plugin add @zhengjunyao/dsh-session-mover

Confirm the layer applied with dsh --profile default --dump-config — see the install guide.

Source

Tags

Readme

dsh-session-mover

把 DSH 的一个对话迁移到另一个工作分区。

Move a conversation between DeepSeek Harness workspaces.

为什么需要插件

DSH 把一个已存储的对话同时绑定到工作分区三次:

绑定 位置 校验
存储目录 ~/.dsh/sessions/<工作分区路径编码>/<会话 id>/session.vN.jsonl.zstd 目录名由工作分区路径推导
会话头 cwd 日志第一帧(独立压缩的 zstd 帧)里的 cwd 字段 assertStoredIdentity 会用 id + cwd 反推期望路径,不匹配就报 corrupt session log
工作分区归属 ~/.dsh/storages/workspace.json 的 sessionIds 加载时按 canonical cwd 过滤,不匹配就从工作分区里消失

所以「把对话挪过去」不是 mv:只改一处,DSH 会认不出这个会话。本插件把三处一并改掉并保持自洽。

安装

# 本地开发(link)
dsh plugin --profile web add link:/path/to/dsh-session-mover

# 发布后(GitHub,仓库带 dsh-plugin topic)
dsh plugin --profile web add github:zhengjy01/dsh-session-mover

# npm
dsh plugin --profile web add @zhengjunyao/dsh-session-mover

装完重启 DSH(或用 @zhengjunyao/dsh-restart 一键重启)。

使用

Web 界面

设置页 →「会话迁移」:

  1. 选一个对话(按工作分区分组,显示标题/时间/大小/是否运行中)
  2. 选目标工作分区
  3. 点预检 —— 显示当前/目标存储路径、会话头 cwd、会被改写的日志数、以及任何阻碍
  4. 勾选「我已核对上面的预检结果」→ 点执行迁移

Agent 工具

工具 作用
session_mover_overview 列出各工作分区及其名下对话 + 未被记账的对话(只读)
session_mover_move 迁移;不传 confirm 只预检,确认后再传 confirm: true 执行
session_mover_history 迁移历史与备份路径(只读)

HTTP 路由(仅回环)

GET  /api/dsh-session-mover/probe           健康探针
GET  /api/dsh-session-mover/overview        工作分区 + 对话 + 最近迁移
POST /api/dsh-session-mover/plan            预检(不写任何东西)
POST /api/dsh-session-mover/move            执行迁移
GET  /api/dsh-session-mover/ledger          迁移历史
POST /api/dsh-session-mover/config          改配置 / 复位
POST /api/dsh-session-mover/backup/delete   删除一个备份目录

安全性

  • 先备份再动手:执行前把整个会话目录复制到 ~/.dsh/dsh-session-mover/backups/<时间戳>-<会话 id>/session/,同时留一份 workspace.json 快照与 meta.json。
  • 失败自动回滚:目录搬迁、头部改写、工作分区归属三步中任何一步失败,都会按相反顺序撤销(先把头部 cwd 改回原值,再把目录移回,最后恢复归属),历史里标注「已回滚」。
  • 只动 sessions 根目录以内:源路径与目标路径都做 realpath + 前缀校验,越界一律拒绝。
  • 拒绝危险情形:会话正在运行中、目标已存在同名目录、目标工作分区目录不存在、会话头没有 cwd、会话目录不唯一 —— 全部拒绝并说明原因。
  • 只替换头部帧:事件帧逐字节原样保留(不重新压缩),所以迁移不可能改写或重排对话内容。
  • 路由仅回环(127.0.0.1/::1 + 同源校验)。

配置

<DSH_HOME>/dsh-session-mover.json(0600;DSH_HOME 未设时回落 ~/.dsh):

{
 "enabled": true,
 "announceToAgent": true,
 "backupRetention": 20,
 "requireConfirm": true
}
  • backupRetention:保留最近多少次迁移的整目录备份(0 = 全部保留)。
  • requireConfirm:Agent 真正执行前是否必须先预检并得到用户确认(默认 true)。

实现要点

  • 帧级外科手术:会话日志是若干独立可解码的 zstd 帧串联,第 0 帧恰好是头部一行。本插件按 RFC 8878 走帧结构定位第一帧边界(不解压正文),只重建第一帧,其余字节原样拼回 —— 8 MB / 两万多帧的日志只需读一次头部。
  • 头部缓存缝:workspaceRegistry 在 init 时按会话 id 缓存头部(headers / sessionPaths)。改写磁盘头部后必须让缓存失效,否则 attachSession 会拿旧 cwd 校验并拒绝。这个内部 Map 是特性探测的(instanceof Map),拿不到就如实报告「需重启」而不是猜。
  • 投影缓存自动失配:identityMatches 把 cwd 纳入生命周期身份,所以头部一改,旧的投影缓存自动不被采用 —— 不需要手动清理。
  • 目录名编码(projectKey / encodeSegment)是 DSH 写入端的逐字节移植,并有对照本机真实目录名的 conformance 测试。

开发

pnpm install
pnpm run typecheck
pnpm test           # 26 项:路径编码 conformance / 沙箱端到端迁移 / 路由层 / 真实日志无损
pnpm run build
pnpm run verify     # 可移植性验证(隔离 DSH_HOME、tarball 安装、起真实实例)
pnpm run verify:live # 真实数据端到端验证(复制真实 workspace.json + 真实会话日志,在隔离实例里跑一次真迁移)

License

MIT