- Главная
- Плагины
- Сеансы и сообщения
- dsh-session-transfer
dsh-session-transfer
xiaolin233-ctrl/dsh-session-transfer
Adds a "Move to another workspace…" row to every conversation menu: copies the full event log into the target workspace under a new session id (header, title and model preset preserved, delivery markers retargeted), reads the copy back to verify it, attaches it, then archives the original unless you keep it.
Установка
dsh plugin --profile web add github:xiaolin233-ctrl/dsh-session-transferREADME
对话转移(dsh-session-transfer)
English: README.en.md
在侧栏的会话「⋯」菜单里多一行 「转移到其他工作区…」:选一个目标工作区,这段对话就会完整地出现在那里,标题、工具调用、token 用量都保留,可以照常打开并继续。
安装
-
把这个仓库放到本机任意目录(路径里有空格也行)。
-
在 DSH 会话里安装本地插件包 —— 目标指向仓库根目录(含
package.json的那层):plugin_manager action: install_bundle target: <仓库绝对路径>装完
application应为applied、warnings为空;插件列表里出现dsh-session-transfer(enabled / active)。 -
刷新浏览器页面加载浏览器半边(Host 半边安装时即生效,无需重启 DSH)。
要求:DSH 支持插件包(Host + Web 客户端),Node.js ≥ 22.5。卸载:plugin_manager action: remove_bundle target: dsh-session-transfer。
仓库就是插件包本身:没有构建步骤、没有运行时依赖,
impl.js每次请求热加载,改完直接刷新页面即可验证。
它怎么保证「不损坏对话」
会话在磁盘上是一段只追加的事件日志(session.v4.jsonl.zstd,header + 每个 append 批次一帧 zstd)。插件从不改写任何已有字节,而是走官方 sessionPersistence API 重放:
- 读源会话的完整日志(活会话先
sessions.flush()落盘,再以存储为准;存储落后于内存时才回退到内存日志)。 - 校验 seq 从 0 连续;中途正在回答的活会话会拒绝,避免复制到半截的回合。
- 用新 id 在目标工作区
create:header 逐字段复制(version/createdAt/isSeeded/delegationDepth/agentPreset/parentSession),只把cwd指向目标工作区;inheritedEventCount原样继承。 - 按 500 条一批
append全部事件,然后flush()。 - 关闭写句柄后重新读回来核对:条数、seq 连续性、末条 seq 必须与源一致,不一致就报告
verify-failed,不会假装成功。 attachSession(新 id)挂到目标工作区(这一步会向侧栏推送更新)。sessionProjectionCache.coldSnapshot(...)补投影缓存 —— 侧栏里冷会话的标题就来自它,不补会显示「未命名对话」。- 默认把原会话归档(
archiveSession):归档只是从分组表面隐藏,未删除任何数据,可在侧栏「已归档」里恢复;勾选「保留原工作区的对话」则完全不动原会话。
因为事件是原样重放的,session/title 事件也在其中,所以标题天然保留;会话目录里只有这一个日志文件,附件在全局存储里,不随会话目录迁移,引用不会断。
一个容易踩的坑:session-log-deepseek/delivery-accepted 事件里记着会话 id(v4 格式要求它与 header 的 id 一致)。原样复制会让副本一读就报 SessionFormatError: current-generation delivery marker names the wrong Session。所以复制时只把这段会话自己产生的(seq >= inheritedEventCount)投递标记改写成新 id;从父会话继承的前缀(副本仍带 parentSession)保持原样。
使用
- 侧栏里把鼠标移到某个对话上,打开「⋯」菜单 → 转移到其他工作区…
- 选择目标工作区,按需勾选:
- 保留原工作区的对话:默认不勾(原对话归档,可恢复)。
- 源工作区有多个对话时会多出 同时转移该工作区全部对话:逐个复制,子代理会话自动跳过。
- 点 转移。完成后可以点 打开新会话 直接进入副本。
子代理会话(header.origin === 'subagent')跟随父会话,单独转移会变成孤儿,因此一律拒绝并单独报错。需要格式迁移的旧会话也会先被拒绝(format-migration-required),避免把旧格式写成新 header。
文件
| 文件 | 作用 |
|---|---|
index.js | Host 入口:只注册 /dsh-session-transfer 的 HTTP 路由,并把 Header 注入所需的 webServer 声明出来。 |
impl.js | 迁移编排与校验:前置拦截 → 读源 → 复制 → 写后验证(含投递标记归属)→ 挂载 → 归档;每次请求都以带戳的 import() 重新加载,所以改这里不用重启 DSH。 |
host-api.js | Host API 适配层:所有 Harness 服务调用、日志读写、投递标记事件类型、可复制格式集合都集中在这里,Harness API 变化时只需改这一个文件。 |
client.js | 浏览器半边:菜单行 + 覆盖层对话框(sidebar.workspaces.session.menu.item / shell.overlay),只用 --dsw-alias-* 主题 token,不依赖 UI primitives。 |
cordis.patch.yml | 把本插件插入 Host composition。 |
locale/{zh,en}.json | 插件管理页里显示的标题与描述。 |
接口(Host 路由)
GET /dsh-session-transfer/state?sessionId=…→{ ok, workspaces:[{id,title,path,sessionCount}], currentWorkspaceId, sourceSessionIds, session }POST /dsh-session-transfer/transfer,body{ sessionIds:[…], targetWorkspaceId, keepOriginal?:boolean, titles?:{id:title} }→{ ok, targetWorkspaceId, targetTitle, targetPath, results:[{ sessionId, ok, newSessionId?, eventCount?, archivedOriginal?, cacheWritten?, note?, code?, message?, partialSessionId? }] }
业务失败放在 results[] 的单项里,不抛 HTTP 错误,这样批量转移里一个失败不影响其他会话。
失败时会发生什么
read-failed/log-corrupt:源日志读不出或不连续 → 不写任何东西。session-running:源会话正在回答 → 等待本轮结束再试。copy-failed:写入中途失败 → 返回partialSessionId,可在存储里找到并清理;原会话不作任何改动。verify-failed:读回核对不一致 → 报告partialSessionId,不挂到工作区。attach-failed:副本已存在但挂载失败 → 返回partialSessionId。- 归档失败(原会话仍在运行等)→ 副本照常成功,
note说明原会话被保留。
验证
插件装好后用一次性自检插件在真实的 Host 进程里跑了两个用例(26 条事件的无种子会话、1250 条事件、inheritedEventCount = 1248 的种子会话),逐项全通过:
- 副本
cwd指向目标工作区,createdAt/isSeeded/delegationDepth/agentPreset/inheritedEventCount全部保留; - 副本事件数组与源一致(投递标记的 id 除外)、seq 从 0 连续、标题保留;
- 副本立即出现在目标工作区的
sessionIds里,投影缓存文件写入成功,侧栏能显示标题; - 自检用的临时工作区、会话目录与缓存均已清理,用户原有工作区未受影响。
Похожие плагины
dsh-web-ui (dsh-chat-recovery)
zhu1090093659/dsh-web-ui
billion-context
ranxianglei/billion-context
dsh-synapse
liangmianya/dsh-synapse
dsh-archive-manager
michengai/dsh-archive-manager