dsh-session-group
DSH 会话管理插件(DeepSeek Harness)
- Stars
- 0
- Language
- JavaScript
- Created
- Aug 18, 2026
- Updated
- Aug 18, 2026
Introduction
dsh-session-group
DSH 会话分组(Workspace)管理插件:创建分组(.dsh/Group 软链接方案)、重命名/删除分组引用、在分组下新建会话(工作移交)、禁止手动新建。设置页一键操作。
机制依据:源码 + 实测三重印证(见下文「DSH 机制事实」)。分组 = 采纳一个目录路径(
workspace.create),项目本体不移动。
功能
- 📁 创建分组:给分组名 → 自动建
.dsh/Group/<title>软链接 →workspace/<title>并采纳为分组;项目实际写在 workspace,不污染 .dsh - 🏷️ 重命名 / 删除:改分组显示名;删除只移除分组引用(目录与会话保留)
- ✨ 在分组下新建会话(工作移交):转发公开 RPC
session.create {workspaceId}→ cwd 自动取分组 path(=workspace 项目目录)→ 自动归属;支持handoff参数把交接说明作为新会话首条消息发出 - 🔄 移动会话 = 归档原会话 + 分组下新建会话(2026-08-18 用户约定):DSH 机制不允许已有会话跨 cwd 移动,正确语义是把工作移交给分组新会话,原会话用 dsh-session-manager 归档(可还原)
- 🚫 禁止在分组下直接新建会话(
blockGroupNewSession,默认 true):服务端拒绝 + 前端隐藏官方分组行的「+」按钮 - 🧭 入口在设置:设置 → 插件配置 → 「会话分组」卡片
安装
mkdir -p ~/.dsh/profiles/web/node_modules/dsh-session-group
cp -r lib package.json cordis.patch.yml ~/.dsh/profiles/web/node_modules/dsh-session-group/
node -e "const fs=require('fs');const p=JSON.parse(fs.readFileSync('~/.dsh/profiles/web/package.json'));p.dependencies['dsh-session-group']='file:./node_modules/dsh-session-group';fs.writeFileSync('~/.dsh/profiles/web/package.json',JSON.stringify(p,null,2))"
cat >> ~/.dsh/profiles/web/cordis.patch.yml << 'EOF'
- insert:
- id: dsh-session-group
name: dsh-session-group
EOF
# 重启 DSH
测试实例(隔离)部署:源码放 node_modules_local/ + file: 依赖 + patch insert + 软链(见 dsh-test-env skill),重启测试实例。
使用(设置 → 插件配置 → 会话分组)
- 创建分组:输入分组名(在配置的
groupRoot下建目录)或绝对路径 → 创建 - 分组列表:显示 标题/path/会话数;可 重命名 / 删除
- 移动会话:填会话 ID + 选目标分组 → 移动(cwd 匹配才成功)
- 分组下新建会话:选分组 → 新建(自动归属该分组;可填交接说明 handoff,作为新会话首条消息)
- 禁止开关:默认开启,勾选后隐藏侧边栏所有分组行的「+ 新建会话」按钮,并拒绝服务端请求
- 移动会话:DSH 机制限制下"移动" = 归档原会话(dsh-session-manager)+ 在分组下新建会话(工作移交)
API
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/session-group/status | 状态(分组数/groupRoot/workspaceRoot/profile/block) |
| GET | /api/session-group/list | 全部分组(含 sessionIds/path/title) |
| POST | /api/session-group/create | 创建分组 {title}(.dsh/Group/ 软链接 → workspace/<title>)或 {path} |
| POST | /api/session-group/rename | 改名 {workspaceId, title} |
| POST | /api/session-group/delete | 删分组引用 {workspaceId}(目录保留) |
| POST | /api/session-group/move | 移动/挂载会话 {workspaceId, sessionId}(cwd 匹配才成功) |
| POST | /api/session-group/new-session | 分组下新建会话 {workspaceId, handoff?}(handoff=交接说明,block 开启时 401) |
| POST | /api/session-group/set-config | 运行时切换 {blockGroupNewSession: bool}(内存生效,持久化需改 patch) |
配置(cordis.patch.yml config)
- insert:
- id: dsh-session-group
name: dsh-session-group
config:
enabled: true
groupRoot: '' # 默认 <dshHome>/Group(.dsh 下)
workspacePath: '' # 默认取默认 workspace 的 path
blockGroupNewSession: true # true=禁止在分组下直接新建会话(默认开启)
DSH 机制事实(源码 + 实测,勿误判)
- 会话归属分组 = 会话 header 的 cwd 硬绑定:只有 cwd 恰好等于分组 path 的会话才能进该分组
Workspace.attachSession强校验realpath(cwd) === pathsession.create {workspaceId}时 cwd 自动设为分组 path → 自动 attach(实测:分组 sessionIds 立即可见)- 分组 path 若为软链接,realpath 后是目标项目目录 → 会话 cwd 落在 workspace 项目(实测 header cwd 验证)
- 已有会话无法跨组移动(实测):
workspace.attachSession未暴露公开 RPC(返回 "not found")workspace.insertSessionBefore跨组拒绝workspace-move-invalid: not accounted- GUI 拖拽只做组内排序(
commitSessionDrag仅同 accountKey)
- 直接改 workspace.json 无效:workspaceRegistry 内存快照无文件 watcher,且
sessionIdsgetter 按sessionPath(id)===path过滤,重启后仍被剔除 - 结论:让分组有会话的正规途径 = 在分组下新建会话(cwd 自动=分组 path);"移动会话" = 归档原会话 + 分组下新建(用户约定)
验证
node test-core.mjs # 10 项断言:create(path/title 软链接)/move(匹配与不匹配)/new-session(含 handoff)/block/list/status/rename/delete
真机验证(测试实例 3083)已通过:
- create 采纳目录 → 分组出现;new-session → 会话自动归属分组(sessionIds 立即可见)
- move cwd 不匹配 → 409
cwd-mismatch(带 hint) - blockGroupNewSession=true → new-session 401
group-new-session-blocked;=false 恢复
已知边界
- cwd 不符的已有会话无法移入分组(DSH 0.1.0-rc.6 机制限制,非本插件可绕);需要时请用「在分组下新建会话」
set-config仅运行时内存生效;持久化需同步改cordis.patch.yml的 config- host 侧 new-session 走 HTTP 回环转发公开 RPC,端口取
PORT/TEST_DSH_PORT环境变量(默认 3081);反代/多实例场景需确认端口正确 - 删除分组保留目录与会话文件(官方语义:只移除侧边栏引用)
- 前端隐藏官方「+」按钮依赖
aria-label^="actions.newSession"选择器,DSH 升级后需复核
License
MIT