dsh-workspace-hot
Create and archive DeepSeek Harness sessions from outside the running Host — no restart, no refresh. 让运行中的 DSH 从外部创建/归档会话。
- Stars
- 0
- Language
- JavaScript
- Created
- Oct 22, 2026
- Updated
- Oct 22, 2026
Introduction
dsh-workspace-hot
让运行中的 DeepSeek Harness 具备「从外部创建 / 归档会话」的能力 —— 不重启、不刷新页面。
dsh plugin --profile web add dsh-workspace-hot
装完重启 app 一次(让插件与它的宿主侧服务加载);此后登记和归档都即时生效,不再需要重启。
简体中文 · English
解决什么问题
桌面版宿主独占 ~/.dsh/storages/workspace.json。任何外部进程去改这个文件,都是在一个看不见的缓存上做手脚:宿主把整张表放在内存里并会原样写回,外部改动要么被静默覆盖,要么更糟——破坏完整性校验,让整个工作区面板加载不出来。
还有第二个容易被忽略的缺口:把会话挂进工作区只更新了成员列表,而客户端另有一张会话摘要表;宿主不发 api-session/added,那一行就不会出现,即使注册表本身是对的。
这个插件从宿主内部把两个缺口一起补上:
- 写入只经过宿主:插件跑在宿主进程里,调用公开的注册表 API(
create/attachSession/archiveSession/unarchiveSession),workspace.json始终只有一个写者。 - 告诉客户端:登记时发出官方客户端消费的
api-session/added摘要事件,会话立刻出现。 - 归档是宿主的持久化写入:归档集由注册表自己发布,UI 自动更新,不需要额外事件;归档从不删除任何文件。
工作方式
外部进程 Host 进程 (插件) 客户端
-------- ---------------- ------
写 requests/<id>.json → fs.watch(requests) 去抖
↓
validateRequest(id, action)
↓
workspaceRegistry.create()
workspace.attachSession()
↓
ctx.emit('api-session/added') → 侧边栏立即出现
↓
读 receipts/<id>.json ← 原子写入回执(成功/失败都会写)
请求是不可变的,且只被确认一次。失败的请求会保留回执,重试请用新的 id。队列就是一个目录,所以任何语言、任何工具都能提交——不开端口、不起 socket、没有鉴权面。
请求协议
任何丢进 $DSH_HOME/dsh-workspace-hot/requests/(默认 ~/.dsh/dsh-workspace-hot/requests/)的 JSON 就是一条请求。先写 <id>.json.tmp 再重命名,避免读到写了一半的文件。
| 字段 | 类型 | 必填 | 含义 |
|---|---|---|---|
version | 1 | 是 | 协议版本 |
id | 字符串 [A-Za-z0-9_-]{1,100} | 是 | 必须等于文件名主干,也是回执文件名 |
action | register | archive | unarchive | 否,默认 register | 操作 |
workspacePath | 绝对路径 | register 必填 | 要创建或复用的工作区 |
sessionId | session-… | register 可选;归档类必填 | 要放入或隐藏的会话 |
title | 非空字符串 | 否 | 工作区新建时的标题 |
stopActivity | 布尔 | 否 | 会话正在跑时强制归档(默认宿主会拒绝) |
回执
写入 receipts/<id>.json:
{ "version": 1, "id": "register-…", "ok": true,
"action": "register", "workspaceId": "…", "workspacePath": "E:\\code\\app",
"sessionId": "session-…", "registered": true, "summaryPublished": true,
"completedAt": "2026-10-22T06:29:41.512Z" }
ok: false 一定带 error。归档回执报 archived(最终状态)与 changed(本次是否真的改动)。
命令行助手(Windows PowerShell)
两个脚本都会先校验 status.json(插件已加载、版本匹配、pid 存活),宿主没就绪就什么都不碰。
# 把会话放进工作区,并发布会话摘要
.\register-workspace.ps1 -WorkspacePath 'E:\code\app' -SessionId 'session-…'
# 隐藏 / 恢复会话(文件保留,位置可还原)
.\session-actions.ps1 -Action archive -SessionId 'session-…'
.\session-actions.ps1 -Action unarchive -SessionId 'session-…'
# 强制归档仍在运行的会话
.\session-actions.ps1 -Action archive -SessionId 'session-…' -StopActivity
# 查看插件状态
.\register-workspace.ps1 -Check
DSH 目录不在 ~/.dsh 时加 -DshHome。
为什么需要「热」登记
派发系统通常把活干在另一个进程里(dsh --profile headless <task>)。那个会话是真实存在的,但它诞生于桌面宿主之外,于是左栏什么都没有。接着只剩两个坏选择:重启 app(对话没了),或者手改 workspace.json(数据可能没了)。
有了这个插件,外部派发者可以:
- 起会话;
- 写一条
register请求; - 它立刻出现在左栏——能点开、能续聊、能归档。
同一条通道也覆盖清理:把一次性会话归档而不删文件,需要时再取消归档回到原位。
兼容性与限制
- 基于 DSH
0.2.0-rc.2桌面版(Cordis 插件 API,ctx.inject/ctx.effect)。 - 注入
workspaceRegistry、sessionPersistence、sessionQuery、sessions、agents。若未来版本改了其中某个名字,插件会激活失败并在~/.dsh/logs/dsh-workspace-hot.log写明原因,而不是半残运行。 - 登记要求会话已落盘(
sessionPersistence.stat),且其 header 的cwd解析后等于目标工作区——这条校验属于宿主,是有意为之。 - 归档不是删除:
session-*文件仍留在磁盘,注册表只是把它从各个分组界面隐藏。 - 文件队列意味着「同机同用户」。它不开网络监听,也没有需要配置的鉴权。
开发
node tests/smoke.mjs
测试跑在临时 DSH_HOME + 假注册表上:登记、摘要事件、重放/幂等、未知会话、非法 action、缺字段、归档与取消归档幂等、生命周期关闭。全程不调用模型。
许可证
MIT,见 LICENSE。