dsh-session-manager-next
DeepSeek Harness(DSH)会话管理插件:在 Web UI 里浏览历史对话、导出对话、整理会话、安全删除。
- 仓库名:
dsh-session-manager-next;npm 包名 / 插件包名仍为dsh-session-manager(安装命令与插件条目 id 不受仓库改名影响) - 适配版本:DSH 0.2.1-rc.2(Desktop / Web 同一套 Host API)
- 形态:Cordis bundle(Host 半 + Web Client 半),无第三方依赖、无需构建
- 参考实现:huajuan2024/dsh-session-manager(面向 0.2.0-rc.2;本插件按其思路针对 0.2.1-rc.2 重写并扩展)
功能
1. 对话导出
| 格式 | 内容 | 适用 |
|---|---|---|
| Markdown | 用户 / 助手消息、思考过程(折叠)、工具调用与结果(代码块)、轮次统计 | 归档、分享、二次编辑 |
| JSON | 完整原始事件日志 + 会话 header + 统计(dsh-session-export v1) |
备份、程序处理、迁移 |
| HTML | 单文件、自带样式与深色模式、可直接双击打开 | 阅读、发给别人 |
- 单会话导出:侧边栏会话「…」菜单 → 导出对话…,或管理面板右侧详情里的三个导出按钮。
- 批量导出:勾选多个会话 → 导出所选 Markdown / JSON(合并为一份文件)。
- 文件名自动带标题、会话 ID 尾段与日期。
- Markdown / HTML 默认省略 Harness 注入的运行时上下文(
Current runtime context.、<system-reminder>等),并在文件头注明省略条数;JSON 保留全部事件。
2. 整理会话
- 重命名标题:走 Host 的
sessionController.rename,durable 写入 session event,不只是本地显示。 - 置顶 / 取消置顶、归档 / 取消归档:走
workspaceRegistry,与侧边栏状态一致。 - 强制归档:会话仍在活动时,可停止其运行并归档。
- 搜索 / 筛选 / 排序:按标题、工作区、会话 ID 搜索;筛选活跃 / 全部 / 空会话 / 已归档 / 已置顶;按最近更新 / 创建时间 / 标题排序。
- 清理空会话:一键筛出
blank会话并批量删除。
3. 删除会话
- 单个删除(详情页二次确认)与批量删除(勾选后确认)。
- 安全护栏:
- 运行中的会话直接拒绝(先停止再删);
- 仍被内存中 Agent 占用的会话需二次确认「强制删除」;
- 删除后同步清理工作区登记(
detachSession)与归档 / 置顶集合,避免残留幽灵条目; - 只删除
<DSH_HOME>/sessions/<项目>/<会话 ID>目录;目标目录名来自readdir结果,永不拼接请求里的字符串;会话 ID 另做白名单校验; - 删除不可恢复:UI 有明确二次确认。
界面位置
- 侧边栏底部图标(面板列表第 4 个)→ 打开整页「会话管理」:左侧会话列表,右侧详情(导出 / 重命名 / 置顶 / 归档 / 删除 + 消息预览)。
- 会话行「…」菜单新增一行「导出对话…」:直接从侧边栏跳到该会话的详情与管理操作。
- 设置 → 插件 中可见「会话管理」条目(标题、描述、图标来自
locale/*.json与icon.svg)。
架构
index.js Host 半:注册 /api-session-manager/{ping,list,detail,export,organize,delete}
- 读:ctx.sessionQuery(readSession / readTitleSnapshots)
ctx.sessionController.list(running / blank / 活动时间)
- 写:ctx.sessionController.rename、ctx.workspaceRegistry(pin / archive / unarchive)
- 删:node:fs 移除会话目录(DSH 0.2.1 未提供删除 API)
- 归档/置顶状态:只读 workspace ledger(<DSH_HOME>/storages/workspace.json)
client.js Client 半:纯 JavaScript 动态模块,注册
sidebar.panellist(图标)+ main(整页面板)+ 会话菜单行
React 由宿主模块表提供;样式只用 --dsw-alias-* 主题 token;
locale 走 ctx.get('locale'),读写只经 Host 路由
test/ render-smoke.mjs:用极简 React/hook/fetch 替身在 Node 中执行整个
Client 半(空态、列表、归档筛选、详情、导出、删除确认、菜单行),
捕获渲染期崩溃与错误路由
为什么删除要落到文件系统
0.2.1-rc.2 的会话生命周期里没有删除接口:
sessionPersistence只有create / open / append / flush / list / stat,没有 delete;sessionController/workspaceController只有archiveSession / unarchiveSession,没有删除;- 侧边栏内置会话菜单只有置顶、重命名、分叉、归档、复制 ID。
因此删除由插件在 Host 进程内完成:扫描 ~/.dsh/sessions/*/<sessionId>(目录名精确匹配,不接受客户端给的路径片段)后 rm -rf,再清理工作区登记与归档 / 置顶集合。
安全模型
- 面板不向模型上下文注入任何内容,不新增 session event。
- Client 只拿到 JSON:没有绝对路径批量下发、没有 token、没有文件系统能力。
- 所有写操作在 Host 进程执行;
sessionId需匹配^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$,且不会进入path.join的可控段。 - Host 路由挂在同源
/api-session-manager/*(与社区插件一致的 Host 路由方式),仅本机页面可访问;接口自身再做参数与状态校验。
安装
从 Releases 下载 dsh-session-manager-0.3.0.tgz 后:
dsh plugin --profile desktop add ./dsh-session-manager-0.3.0.tgz
也可以直接安装仓库目录(开发时更方便):
# 方式一:插件管理器(Web UI 设置 → 插件 → 添加,选择本目录)
# 方式二:CLI
dsh plugin --profile desktop add <本目录绝对路径>
安装后 Host 会热加载 Client bundle,侧边栏出现「会话管理」图标;若未出现,刷新页面。
包名
dsh-session-manager与上游同名项目一致,本仓库以本地 bundle 方式安装,不发布到 npm(package.json保持private: true,避免名字冲突)。
配置
无必填配置,默认值:
| 项目 | 默认值 |
|---|---|
| 会话根目录 | $DSH_HOME/sessions(DSH_HOME 缺省 ~/.dsh) |
| 工作区账本 | $DSH_HOME/storages/workspace.json(只读) |
| 单次请求体上限 | 128 KiB |
| 单次批量删除上限 | 500 |
测试
node test/render-smoke.mjs
无需安装依赖:测试自带 React / DOM / fetch 替身,覆盖 Client 半的注册、渲染与请求路径。
已知边界
- 删除正在内存中的会话后,侧边栏条目要等页面刷新或 Harness 重启才消失(Host 的 workspace/session 索引在启动时构建)。
- 归档 / 置顶的读取依赖 workspace ledger 的结构;结构变化时仅失去状态显示,写入仍走官方 API。
- 批量导出 HTML 会退化为 Markdown(单会话 HTML 完整支持)。
- 「…」菜单行通过注入的
useMenuOpenState关闭菜单;宿主改变该 hook 名称时,行仍可点击,只是菜单需手动关闭。 - 国际化:简体中文 / English。
卸载
设置 → 插件 → 会话管理 → 卸载,或:
dsh plugin --profile desktop remove dsh-session-manager
验证状态(本机 0.2.1-rc.2)
Host 路由已用真实 HTTP 调用验证:ping、list(真实会话列表,归档 / 置顶 / running 状态正确)、detail(含运行中会话与导入会话)、export(Markdown / HTML / JSON / 批量均正确返回文件)、delete 与 organize 的守卫(不存在、运行中、内存中、非法 ID、非法 action、错误方法均返回对应错误码)。Client 半的三处注册在运行中的页面里均为 active,渲染与交互由 test/render-smoke.mjs 覆盖;面板的实际视觉效果需在浏览器里目视确认。
No comments yet. Be the first to write one.