description: "Web 会话总览:把所有工作区里的会话按天汇成一页,可搜索、可排序;注册一个全局主面板与一个侧栏入口,不替换任何官方界面。"
kind: "package-reference"
dsh-session-hub
English | 中文
摘要
一个第三方 DeepSeek Harness 插件:把所有工作区里的所有会话列在同一页,默认按天排列,支持按标题与工作区路径搜索、运行中与含归档筛选,并用每个工作区固定的一种颜色贯穿所有排序。官方侧栏是「先按工作区分组」的浏览方式;这一页提供另一条路径——按时间找,按工作区认——面向工作区和会话都已经多到分组列表一眼看不清的安装。它只注册 keyed 槽位 main 的面板 session-hub 与同 id 的 sidebar.panellist 入口,读取已有的 sessions.list 与 workspaces.list 快照,不覆盖任何官方界面。
目录
- 使用方式
- 实现说明
- 模型侧可见性
- 已知限制
- 点个 Star
- 维护者备注
使用方式
装进某个 profile 并启用该 bundle,侧栏「任务」入口旁会多一个图标;点它会在主栏打开这一页,不会选中或激活任何会话。
dsh plugin --profile desktop add github:20zxknk/dsh-session-hub
页面不需要任何配置,也不注册设置项。页头写明会话数量以及这些会话涉及的工作区数量;旁边的四档排序是页面唯一的模式开关:按日期(默认)、最近活动、按工作区、按标题。
日期视图
按日期把列表按本地日期分组,组标题吸顶——今天、昨天,再往后是「月日」,跨年后自动带上年份——每行左侧是该会话最后一次提问的本地时刻。同一天内按时间倒序。这就是这个插件存在的理由:人回忆一个会话时,通常想的是「周二下午在那个项目里弄的」,组标题加时刻列不需要读任何标题就能回答这一点。
按工作区按工作区的存储标题分组、把完整路径作为组说明,顺序跟随工作区自身顺序,不属于任何工作区的会话收在 未归入工作区 下。最近活动与按标题是平铺列表;按标题按会话标题排序、同标题时回落到时间,因此未命名会话会聚集在末尾而不是散在中间。
搜索与筛选
搜索框同时对三个值做大小写不敏感的子串匹配:会话标题、所属工作区标题、所属工作区的存储路径。因此输入一个目录名就能把页面收窄到那个工作区,不需要额外的「工作区」筛选器;输入会话 id 片段不会命中,因为 id 从不进入这一页。Escape 只清空非空查询,不关闭页面。
两个筛选条:运行中只保留有任务在跑的会话;含已归档把已归档会话加入列表,否则它们隐藏。已归档的行保持它在当前排序里的位置、整体变淡并带 已归档 标签;被固定的会话带 固定 标签,且在各档排序中保持固定,而不是被提到最前。
排序、两个筛选——且只有这三项——保存在浏览器本地的 dsh.session-hub.view 下,刷新后页面恢复原样。查询条件刻意不保存:一个把列表清空的陈旧筛选,比重新输入一遍更糟。
行与辨识信息
每一行都是一个按钮,通过 uiWorkspace.openSession 打开对应会话;每行带四个辨识值:工作区色条与色点、会话标题、工作区标题、轮次。颜色由工作区 id 做稳定哈希得出,因此同一个工作区在刷新前后、在各档排序里、在它的每一个会话上都保持同一个色相;同一个色相也用在该行元信息里的小圆点上——这是让「两个不同工作区的会话」在读标题之前就能区分开的东西。轮次来自会话的 sessionStats 投影,投影缺失或轮次为零时不显示,避免把刚建的草稿会话看成干了活的会话。
没有标题的会话显示本地化的 未命名(斜体灰字),而不是空的标题行;悬停一行会显示绝对时间戳与工作区路径。origin: 'subagent' 的会话与未开始的新建空会话不出现:它们应当经由父会话寻址,而不是作为历史行。
分批
页面一次最多渲染 200 行,显示更多 追加下一批,页脚说明还剩多少行。排序、筛选或查询一旦变化,上限即复位,新视图不会继承上一次的滚动深度。
实现说明
实现细节——点击展开
整个 bundle 是一个浏览器包,Host 半边两行、没有构建步骤;client.js 是普通 JavaScript,注册一个 id 等于包名的 module-loader factory。Host 的 apply 存在只是为了让该行能被解析。
Client 半边注册两个槽位并注入 slots、locale、sessions、workspaces、uiWorkspace。ctx.slots.inject('main', ...) 注册 keyed 面板 session-hub;ctx.slots.inject('sidebar.panellist', ...) 以同一 id、order: 20 注册侧栏入口,侧栏「面板 id 指向同名主面板键」的规则让这个图标完成跳转。两个注册都在 slots.inject 之后,因此跟随声明槽位的生命周期,并在声明恢复后重新注册。
数据来自框架自身的、与框架无关的快照——ctx.sessions.list 与 ctx.workspaces.list——inject 面把它们转成基于 useSyncExternalStore 的选择器 hook。当宿主提供标准 useSessions / useWorkspaces prop 时面板优先使用它们,否则回落到注入的 hook;实时运行状态读标准 useSessionStatus hook,宿主不提供该 hook 时回落到摘要自身的 running 字段。插件没有自己的 store、轮询或缓存:页面上的每个数字都是侧栏已经在读的那份快照的纯投影,侧栏已经列出的会话不会让这一页多付任何读取成本。
渲染分为「行投影」与「分组投影」两步。行在快照或筛选变化时构建一次,一趟完成过滤与排序,之后再分组;行组件除 props 外不持有状态,因此时钟走一格只重绘标签并重算组标题,不重建行模型。页面只保留一个每 60 秒推进「现在」的 setInterval,供平铺排序里的相对时间标签使用,日期视图的组标题也从同一个值重算,所以页面开着跨过午夜会自己换标签,不需要刷新。搜索、排序、筛选状态放在 useState;只有这三项视图选择会写入 localStorage。
页面自带 error boundary。该页内部渲染出错时只渲染一句本地化提示并打日志,槽位仍然被占用,不会把主栏打成空白。
文案统一走 Client locale 服务、命名空间 sessionhub,en 与 zh 两份字典齐全;侧栏标签是每次投影都重读当前语言的 thunk。样式只使用 --dsw-alias-* 主题 token,因此跟随明暗主题而不需要自己的覆盖;工作区色相是唯一生成出来的颜色,取中亮以在两种背景上都清晰。
模型侧可见性
无。插件不注册工具、命令、提示词段落或会话事件,也从不向会话日志追加内容;它只读取会话所属包已经发布的列表投影。
KV Cache 影响
无。该页面的数据不进入任何 provider 请求。
已知限制
- 列表里没有内容。 行只显示元信息。由首条提问生成的标题会被 Host 截到 20 字上下,所以标题相撞的会话——例如同一段提示词反复跑过——只能靠工作区、日期、时刻、轮次区分。要显示提问摘要就得逐会话读取历史,这一版刻意不做。
- 没有会话生命周期操作。 这一页只负责打开、排序、筛选、搜索;不删除、不归档、不重命名、不导出、不 fork。那些动词留在侧栏与拥有它们的包里。
- 没有虚拟滚动。 渲染按 200 行分批,几千个会话仍然流畅,但不是为几万行设计的。
- 已归档与空白会话默认不可见,空白会话在这一页完全无法到达。
- 实时状态只有「运行中」,没有「待处理」。 宿主提供状态 hook 时圆点跟随真实运行状态;它不会报告某个待批准的审批或待回答的提问。
点个 Star
如果这一页在你侧栏里占到了位置,★ 给 dsh-session-hub 点个 Star —— 下一个有四十个工作区的人就是靠它找到这里的。
维护者备注
维护上下文——点击展开
在 DeepSeek Harness 0.2.1-rc.3 上开发并实测,在 0.2.1-rc.5 上重新确认过注册与数据流,环境为 Desktop profile。这两个版本之间注册方式与数据流都稳定;0.2.2 的变更清单里提到自定义插件要迁移到异步的会话投影句柄,本插件没有使用该接口——它只读列表快照——预计不需要迁移,但那个版本尚未实测。
真正有意思的约束都是官方侧栏已经解决过的那几个:列表快照不是有序目录,而是快照加一个主视图保留标记;固定集与归档集以两个 id 数组的形式到达,而不是行上的字段;会话所属工作区必须扫描每个工作区的 sessionIds 才能确定,因为没有任何一行携带自己的归属。这三点决定了 buildRows 的形状,也是将来重构该从哪里下手的地方。