dsh-session-status-hub —— 会话状态中心状态栏 / Windows 通知 / 任务栏状态点
29.9/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/28 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add railgun20001/dsh-session-status-hub该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:仅索引
图例:✓ 通过! 需注意— 未判定 / 无数据· 「未判定」= 本站尚无数据,不代表插件有问题
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
DeepSeek Harness 的会话状态中心:顶栏一条轨道显示运行中、等待中、未读、失败的会话,Windows 弹出通知,任务栏还有红黄绿状态点。
README
由 DeepSeek 最新模型翻译生成dsh-session-status-hub —— 会话状态中心(状态栏 / Windows 通知 / 任务栏状态点)
English · 简体中文
给 DeepSeek Harness 补一个「所有对话现在怎么样了」的统一视图:
- 软件顶端标题栏的状态栏:按颜色顺序 红(报错)> 黄(等待回答)> 绿(待查看)> 蓝(运行中) 排列当前需要关注的对话,点一下直接跳到该对话;
- Windows 通知:对话状态发生变化(完成 / 失败 / 需要你回答)时弹系统通知,点击通知跳回对应对话并尽量把 DSH 窗口提到前台;
- 任务栏状态点:用 ITaskbarList3.SetOverlayIcon 在任务栏的 DSH 图标上画红 / 黄 / 绿点,优先级 红 > 黄 > 绿,窗口最小化或在别的应用前面时也能看到——包括正在看的这个对话在后台跑完(官方不会给主视图会话点亮,插件自己补,见 1.2 节)。
展开完整文档(README 剩余 1 节)
dsh-session-status-hub —— 会话状态中心(状态栏 / Windows 通知 / 任务栏状态点)
English · 简体中文
给 DeepSeek Harness 补一个「所有对话现在怎么样了」的统一视图:
- 软件顶端标题栏的状态栏:按颜色顺序 红(报错)> 黄(等待回答)> 绿(待查看)> 蓝(运行中) 排列当前需要关注的对话,点一下直接跳到该对话;
- Windows 通知:对话状态发生变化(完成 / 失败 / 需要你回答)时弹系统通知,点击通知跳回对应对话并尽量把 DSH 窗口提到前台;
- 任务栏状态点:用 ITaskbarList3.SetOverlayIcon 在任务栏的 DSH 图标上画红 / 黄 / 绿点,优先级 红 > 黄 > 绿,窗口最小化或在别的应用前面时也能看到——包括正在看的这个对话在后台跑完(官方不会给主视图会话点亮,插件自己补,见 1.2 节)。
1. 状态定义
| 状态 | 判定依据(客户端受支持服务) | 状态栏 | 任务栏 |
| 报错 error | 宿主投影 hubTurnOutcome.outcome ∈ error / interrupted,且该 seq 未被确认 | 🔴 红点 + 「报错」 | 红(最高) |
| 等待回答 answer | uiSession.sessionStatus.pendingInteraction.kind ∈ approval / plan-review / question | 🟡 黄点 + 「等待回答」 | 黄(无红灯时) |
| 待查看 unread | sessionStatus.completionUnread === true(原生「未读完成」绿点)或本插件补记的「后台完成」 | 🟢 绿点 + 「待查看」 | 绿(无红/黄时) |
| 运行中 running | sessionStatus.running 或会话行 running | 🔵 蓝点 + 「运行中」 | 不点灯 |
- 状态栏 chip 的显示顺序 = 颜色顺序:红 > 黄 > 绿 > 蓝(STATE_ORDER),同一类里更新时间新的靠前。
最需要你介入的排最前,正在跑的排最后——所以默认只显示的 3 条永远不会被「运行中」占位。
- 判定顺序(一个会话归到哪一类,取第一个命中的)与显示顺序无关:等待回答 > 报错 > 运行中 > 待查看。既在等你回答、上一轮又还没确认失败时按「等待回答」显示。
- 空白「新会话」占位行与子代理会话(origin: subagent)不进入状态栏,也不通知。
- 「报错」在点击该条目或正看着该会话且窗口聚焦时自动确认(记下 seq),不会永远亮着;同一个会话出现新的报错会重新亮起。
- 「待查看」优先复用原生 completionUnread,打开会话即熄灭;当前对话在窗口不在前台时跑完的那一轮由插件自己补记(见 1.2 节),回到前台并正在看它时熄灭。
任务栏聚合规则:只要有一个报错就是红灯;否则有等待回答就是黄灯;否则有待查看 就是绿灯;都没有就清除覆盖图标。
运行中不点灯。正在跑是常态,不需要你介入;如果运行中也点亮任务栏,绿灯几乎常亮,真正的红/黄提示反而被盖住。运行中仍然出现在状态栏 chip 里(蓝色脉冲点)。
1.1 会话行状态点:现状与同步
侧栏每行行首那个圆点是官方 ui-workspace 画的,插件改不了;这里把它的来源列清楚,并说明本插件在哪一层补上「失败」这个官方缺失的语义。
官方行首圆点(SessionStatusDots → StateDot)
判定顺序(ui-workspace 的 sessionStatuses,取第一个作为行首点):
| 优先级 | 条件 | 圆点 | 说明 |
| 1 | pendingInteraction.kind ∈ approval / plan-review / question | 🟡 warning | 等你批/等你答 |
| 2 | sessionStatus.running(含子代理运行) | 🔵 ongoing | 正在跑 |
| 3 | completionUnread === true | 🟢 done | 「已完成未查看」 |
| 4 | 以上都不是 | — | 空闲;此时行首位置才轮到插件插槽 sidebar.session.row.leading |
已知错位:失败/中断的会话也显示绿色「已完成」
ui-session 的 observeRunning 在「running → 停止」时无条件把会话记成 completionUnread,不区分这一轮是正常完成、报错还是中断。于是:
- 一轮因 503 / 中断结束的会话 → 行首照样是绿点「已完成」;
- 官方行状态机里没有 error 态,插件也无法覆盖非空闲行首(sidebar.session.row.leading 只在第 4 档才渲染)。
本插件的同步方式
- 不改官方圆点(改不了,也不该去碰别人的 DOM);
- 在我们能控制的最近位置补上真相:sidebar.session.row.hover(行悬停卡片的内容位, list 插槽)里加一行红色提示——本轮失败 / 本轮中断,带投影里的 code(若有);
- 判据与状态栏完全一致(failureKindOf),已确认的失败不再提示;
- 打开该会话即成为 main view,官方 completionUnread 会清掉,绿点自然消失。
想让行首圆点本身变成红色「失败」,需要上游在 ui-session 里区分失败结局(或给行状态机加 error 态)——这不是插件能做的,本插件只负责不再让用户被绿点误导。
1.2 当前对话在后台跑完:为什么绿点要插件自己补
官方 ui-session 的 observeRunning 只在「running → 停止」且该会话不是主视图 (retainedBy.mainView === 0)时才记 completionUnread,reconcileStatus 还会对主视图会话主动清掉它。于是最常见的等任务姿势——正开着这个对话、切去别的应用干活、任务在后台跑完——在官方状态里是「什么都没发生」:没有绿点、没有绿灯、状态栏也不会出现条目。
插件的补法是同一份边沿、两份用途(planNotifications 的 completions 返回值):
| 观测事实 | 窗口在前台 | 窗口不在前台(被盖住 / 最小化 / 别的虚拟桌面) |
| 当前对话的回合结束(completed / max-tokens) | 静默(结果就在眼前),不点灯 | 弹「DSH · 任务完成」+ 补记「待查看」→ 绿点 + 任务栏绿灯 |
- 「窗口在不在前台」由 document.visibilityState === "visible" 且 document.hasFocus() 判定(requireVisibleForFocus 控制前者),一次观察只判定一次,状态栏 / 通知 / 记账同源;
- 记账落盘在 localStorage 的 dsh-session-status-hub/local-unread-v1({ [sessionId]: seq }),刷新页面后绿点仍在;
- 熄灭条件:窗口回到前台且正在看这个会话,或用户点了它的通知 / 状态栏 chip (openSession 即视为已查看)。窗口焦点变化会主动触发一次观察,不用等下一次会话推送;
- 失败 / 中断不走这条路:它们本来就有「报错」红灯(红 > 绿),不需要再补绿点。
2. 界面位置
桌面端:状态栏注册在官方 shell.overlay 浮动层(官方声明它是单占位、replaceRisk: none,插件无需替换任何既有 UI),固定在软件顶端标题栏、「应用 / 编辑」菜单之后、窗口控件之前,随顶栏高度(40px)垂直居中;菜单被侧栏收起改变位置时,用 --dsh-windows-menu-start 自动跟随。
该层本身点击穿透,状态栏条目自己恢复 pointer-events 并标记 -webkit-app-region: no-drag,所以 chip 可点、标题栏其余空白仍可拖动窗口。
Web / 非 Windows 顶栏环境:同一组件回落到官方 conversation.header.leading(同为单占位插槽),位于会话标题之前、工作区内容之上。
- 每条是一个圆角 chip:状态点 + 会话标题 + 状态短标签;标题过长省略,悬停显示完整标题;
- 默认只显示优先级最高的 3 条;还有更多时显示 +N,点一下展开全部,再点「收起」恢复,既不挤压标题,也不会把第 4 条截成半截;
- chip 横向排列,超出宽度可横向滚动(隐藏滚动条),不挤压标题;
- 全部空闲时显示一个低调的「全部空闲」;没有可用会话(或插槽钩子缺失)时也不会报错;
- 视觉只用 --dsw-alias- / 设计系统 token,浅色/深色主题都跟随宿主。
会话行悬停卡片:另外注册到 sidebar.session.row.hover(list 插槽),只在某行「最后一轮失败/中断且未确认」时追加一行红色提示(本轮失败 / 本轮中断 + code),用来解释官方行首那个绿色「已完成」点(见 1.1 节)。
点击 chip 的行为:确认该条目的报错 → uiWorkspace.openSession(sessionId)。
chip 点击不请求窗口前置:窗口本来就在前台,而且这样可以完全避免触碰窗口状态——旧实现无条件调用 SW_RESTORE,会把最大化的 DSH 窗口打回普通尺寸,这就是「点一下对话最大化被取消」的根因。通知点击也只在窗口确实不在前台时才请求前置;宿主半现在按 IsIconic / IsZoomed 分支,最小化才 SW_RESTORE,最大化用 SW_SHOWMAXIMIZED 保持。
跳转后左侧会话列表也会滚动到该会话行:官方 ui-workspace 的列表 reveal(展开折叠分组 + scrollIntoView)只挂在侧栏搜索结果点击上,公开的 uiWorkspace.openSession() 只切换主视图、不滚动列表——长列表里目标行可能在视野外,点了 chip 左侧看不出变化。
插件因此自己补一次「滚到行」(src/hub-scroll.js):按官方列表行的 data-row-key
(session:<id>)定位,scrollIntoView({ block: "center" }) 把目标行滚到列表垂直居中
——一次显式跳转,目标落在中部最醒目;列表首尾的行无法真正居中时浏览器会自动收敛到可滚动边界,即「尽量居中」。会话行始终找不到(分组被折叠 / 被归档过滤)时退化到它所属的 workspace:<id> 分组行。跳转后列表要等 React 提交才渲染目标行,所以用 rAF 重试到 1.5 秒;找不到就静默放弃,任何异常都被吞掉,绝不影响跳转本身。
3. Windows 通知
| 通知 | 触发事实 | 默认 |
| 需要你回应 | pendingInteraction 出现或换成新的 key | ✅ |
| 任务失败 | 新回合结局 error / interrupted(含账号登出钩子) | ✅ |
| 任务完成 | 新回合结局 completed | ✅ |
| 达到输出上限 | 新回合结局 max-tokens | ✅ |
| 任务已停止 | 新回合结局 aborted(自己按的停止) | ❌ |
不会为「正在思考 / 正在执行工具」这类实时活动弹通知;这正是多数通知插件最吵的地方。
「开始运行」默认也不通知——通常是你自己刚发的消息,没有任何新信息值得打扰。
窗口不在前台时,「当前对话」跑完也照常弹:静默只发生在「窗口在前台且正在看这个会话」的时候(silenceCurrentWhenFocused)。所以切去别的应用干活、任务在后台跑完 → 通知 + 任务栏绿灯 + 状态栏「待查看」三件一起到(见 1.2 节)。
点击通知:openSession 跳转 + 宿主半前置窗口。每条通知一个唯一且足够短的 tag (dsh + 12 位十六进制哈希 = 15 个字符,取自 <插件>:<会话 id>#<事件键>)。
多标签页共享 localStorage 时同一事件只弹一条。
通知上的颜色标识
标题前带一个彩色圆点,与状态栏 chip、任务栏状态点同源同色,扫一眼就知道是哪一类:
| 类别 | 标识 | 标题 |
| 完成 | 🟢 | DSH · 任务完成 |
| 需要你回应(提问 / 待审批 / 计划待审) | 🟡 | DSH · 需要你回应 |
| 失败(含中断) | 🔴 | DSH · 任务失败 |
| 达到输出上限 | 🟠 | DSH · 达到输出上限 |
| 手动停止(默认不通知) | ⚪ | DSH · 任务已停止 |
| testNotification() 测试通知 | 🔔 | DSH · 通知测试 |
为什么是 emoji 而不是彩色图标:Windows 的 toast 图标固定取应用图标(AUMID 对应的快捷方式),HTML5 Notification 的 icon 在 Windows 上不会落到 toast 里,badge 更是只在 macOS / Android 实现。要换图标只能放弃 HTML5 Notification、改用宿主半直接发 WinRT toast,但那样点击就回不到页面里(见第 3 节的取舍),所以颜色只能进文本——Windows 用 Segoe UI Emoji 把圆点渲染成真实彩色字形。
不想要圆点:__DSH_SESSION_HUB__.setConfig({ colorMarkers: false })。
为什么 tag 必须是短哈希(2026-10-26 定位)
Windows 的 toast 对 tag 长度有硬限制,超了会让整条通知在 Chromium 内部被静默丢弃: new Notification() 构造成功、show 和 failed 都不派发、Windows 的 PushNotification-Platform 连 2416(本地通知提交)都没有——表现和「插件压根没发」完全一样。同机对照实验(Windows 11 + Electron 44.7.0,都在渲染进程里 new Notification):
| tag | 结果 |
| 不带 tag | show,Windows 有 2416 |
| ab-x(4 字符) | show,Windows 有 2416 |
| abcdefghij123456(16 字符) | show,Windows 有 2416 |
| dsh-session-status-hub:<会话 id>#completed:<seq>(示意,远超 16 字符) | 无 show、无 failed、Windows 无 2416 |
所以 tag 只放哈希(15 字符,低于实验安全上限 16),可读身份记在诊断日志的 key 字段里。
去重不依赖 tag:同一事件只弹一次由 claimEvent + 水位负责。
2026-10-23 曾把同一现象归因为「平台的替换 / renotify 分支」,并据此加了「唯一 tag」;实际根因是 tag 太长,唯一的可读 tag 依然几十个字符,所以那段时间通知照丢。
诊断日志
通知到底有没有落到系统上,看客户端半的落地日志(localStorage 的 dsh-session-status-hub/notify-log-v1,保留最近 20 条):
| stage | 含义 |
| shown | 浏览器确认已显示 |
| failed | 浏览器/系统明确拒绝(Electron 派发 failed;HTML5 叫 error,两个事件名都挂) |
| permission / unsupported | 权限没给 / 环境没有 Notification |
| deduped | 同一事件已被别的标签页弹过 |
| construct-failed / hook-failed | 构造或挂回调时抛异常(带 detail) |
| no-event | 2.5 秒内什么事件都没来,只是「未确认」,不能当成功也不能当失败,更不能用它触发重发 |
要确认系统侧,查 Microsoft-Windows-PushNotification-Platform/Operational 里的 2416 / 3052 / 3153 事件——没有 2416 就说明通知根本没离开 Electron。
4. 任务栏状态点(实现说明)
Electron 的 BrowserWindow.setOverlayIcon 没有暴露给渲染进程,preload 里也没有任何任务栏 API。本插件走的是宿主半 + PowerShell worker + Win32:
渲染进程 宿主半(Electron 主进程的子进程) │ POST dsh-session-status-hub/taskbar │
│ {state: red|yellow|green|none} │
└──────────────────────────────────────▶│ createWindowRaiser.overlay()
│ ├─ EnumWindows(ppid) 找主窗口 HWND │ └─ stdin: overlay:red:13944 ▼ 常驻 PowerShell worker 预生成 3 个 HICON(红/黄/绿,32×32,白描边)
ITaskbarList3.SetOverlayIcon(hwnd, hicon, "DSH red")
- 跨进程调用已实测有效:宿主进程是 Electron 主进程的直接子进程,process.ppid 就是持有窗口的 DeepSeek Harness.exe;ITaskbarList3 以 HWND 为目标,由 Explorer 绘制。
- worker 在启动时一次性 Add-Type 编译 C#(约 6 秒),之后每条指令只是写一行 stdin;状态点亮着时不会因空闲退出(否则 HICON 随进程销毁,任务栏点可能消失)。
- 插件卸载 / 页面卸载 / pagehide 都会发 none 清掉状态点,避免红灯残留。
- 只有桌面外壳(location.protocol === 'dsh-app:')会发任务栏请求;浏览器里的 Web 页面不会互相抢状态。
颜色优先级:red(任一报错)> yellow(任一等待回答)> green(任一待查看)> none。
绿灯的「待查看」有两个来源:官方 completionUnread(非主视图会话),以及插件为 当前对话在窗口不在前台时跑完那一轮补的记账(官方对主视图会话永不置位,见 1.2 节)。
5. 架构
plugins/dsh-session-status-hub/
├── index.js 宿主半:hubTurnOutcome 投影 + 路由(ping/debug/raise/warmup/taskbar) ├── lib/ │ ├── raise-window.js 宿主半:窗口前置 + 任务栏状态点(PowerShell worker) │ └── client.js 客户端 bundle(build/bundle.mjs 生成,勿手改) ├── src/ │ ├── hub-identity.js 插件 id / 投影键 / 路由前缀 / 签名头 / 存储键(两半共用) │ ├── hub-strings.js 界面文案 zh/en(单一来源) │ ├── hub-model.js 状态栏 / 任务栏纯逻辑(可单测) │ ├── notify-core.js 通知边沿引擎(纯逻辑,可单测) │ ├── hub-scroll.js 侧栏会话行定位、滚动与揭示控制器(可单测) │ ├── hub-json-store.js localStorage JSON 读写(读失败回落、写失败只告警) │ ├── hub-stores.js 配置 / 水位 / 报错确认 / 后台完成记账 │ ├── hub-notify.js 通知落地:短 tag、跨标签页去重、落地诊断日志 │ ├── hub-host.js 宿主半路由:前置窗口 / 任务栏状态 / 在线探活 │ ├── hub-ui.js React 状态栏与行悬停提示组件 │ ├── hub-slots.js 三处插槽注册 │ └── client-main.js 接线:服务 → 模型 → 通知 / 状态栏 / 任务栏 ├── build/bundle.mjs 把 src/ 下的源文件按顺序内联成单文件浏览器 bundle(--check 校验) ├── test/ 138 个用例(node:test,零依赖) ├── locale/{zh,en}.json 插件管理器的标题与描述 ├── icon.svg 插件图标 ├── cordis.patch.yml bundle patch:插入 session-hub 行 ├── .github/workflows/ci.yml CI:产物与源码一致性校验 + 全部用例 ├── LICENSE MIT └── README.zh-CN.md 本文件(英文门面见 README.md)
为什么事件要自己算边沿:DSH 给客户端的两路数据都是「当前状态」而不是事件流(running 是布尔、pendingInteraction 是当前值、投影是「最后一个回合的结局」)。
通知必须由状态边沿推导,不能每收到一次推送就弹一次。边沿只有一份实现(src/notify-core.js 的 planNotifications),它同时产出两样东西:该弹哪些通知 (notifications)和这一轮真的结束了哪些会话(completions)。后者与通知开关、静默无关,用来补官方缺失的「待查看」绿点(见 1.2 节)。
为什么失败要靠宿主投影:客户端拿不到「上一轮为什么结束」。turn/end 事件带 reason.kind,但只在会话被打开(retain)时才有事件流。会话投影是受支持的正解:框架驱动所有会话(含未打开的冷会话)折叠,值经三条受支持通道到达客户端(会话列表 RPC 的 projections 块 / session.projections RPC / 打开会话时的尾页基线)。
因此本插件不 retain 任何会话。
启动基线:插件刚激活时已有状态只记为水位、不弹通知;只有真正的新边沿才通知。
事件时间早于 2 分钟(freshnessWindowMs)的边沿只推进水位,宿主重启后补写的历史事件不会变成一串迟到的通知。
6. 安装
命名说明:仓库名、npm 包名、插件内部 id(路由前缀 / localStorage 键 / 日志标签)统一为 dsh-session-status-hub。0.1.x 曾用 dsh-session-status-hub,升级时旧存储键会自动搬过来。
方式一:npm
npm install dsh-session-status-hub
方式二:从源码装。把仓库克隆(或下载解压)到本机任意目录:
git clone https://github.com/railgun20001/dsh-session-status-hub.git
然后二选一:
- 用 DSH 的「插件」页 → 添加本地插件 → 选择 dsh-session-status-hub 目录;
- 用插件管理器(如果所在会话提供 plugin_manager 工具)→ install_bundle → 目标指向该目录。
安装后:
- 宿主半(index.js)由 HMR 在 profile 组合变化时加载,无需重启;
- 客户端半(lib/client.js)需要刷新页面:在 DSH 窗口按 Ctrl+R,或完全重启 DSH。
卸载:插件管理器移除 dsh-session-status-hub bundle,再刷新页面;宿主半卸载时会自动清掉任务栏状态点。
7. 配置
配置在 localStorage 的 dsh-session-status-hub/config-v1,默认值已经覆盖「不打扰」:
插件在同一前缀下还落了几份运行状态(排障、迁移或清理数据时需要知道它们的存在):
| 键 | 内容 |
| config-v1 | 下面这张表的配置 |
| watermarks-v1 | 通知水位:每个会话已消化到哪个事件(决定「哪些算新事件」) |
| error-acks-v1 | 已确认的报错 { 会话: seq }(红点 / 任务栏红灯据此熄灭) |
| local-unread-v1 | 补记的「后台完成、尚未查看」{ 会话: seq }(绿点 / 绿灯的依据) |
| recent-v1 | 跨标签页去重窗口(4 秒内的同一事件只弹一次) |
| notify-log-v1 | 最近 20 条通知的落地结果(见第 3 节诊断日志) |
| 键 | 默认 | 含义 |
| enabled | true | 总开关(关掉后仍推进水位,重新打开不会补弹历史) |
| onComplete / onFailure / onAnswer | true | 完成 / 失败 / 需要你回应 |
| onMaxTokens | true | 达到输出上限 |
| onStopped | false | 手动停止 / 中断 |
| includeSubagents | false | 子代理会话是否也通知与显示 |
| silenceCurrentWhenFocused | true | 聚焦且正在看该会话时不弹 |
| silenceAllWhenFocused | false | 聚焦时一律不弹 |
| requireVisibleForFocus | true | 最小化/隐藏视作不聚焦(照常弹) |
| freshnessWindowMs | 120000 | 超过这个时长的事件只推进水位、不通知(0 关闭) |
| sound | true | 系统提示音 |
| raiseWindow | true | 点击通知 / chip 时请求宿主半前置窗口 |
| colorMarkers | true | 通知标题前的彩色圆点(见第 3 节) |
| maxRememberedSessions | 300 | 通知水位表最多记住多少个会话(按 updatedAt 保留最新的) |
在 DSH 窗口 DevTools 控制台里改:
__DSH_SESSION_HUB__.getConfig()
__DSH_SESSION_HUB__.setConfig({ silenceAllWhenFocused: true })
enabled / onComplete 只关通知,不影响绿点与任务栏:绿点补记是状态事实(「这一轮跑完了、你还没看」),不是打扰。
8. 诊断与验证
cd path/to/dsh-session-status-hub
npm run check # 1) 校验 lib/client.js 与源码一致 2) 跑 138 个用例
DSH 窗口 DevTools 控制台:
__DSH_SESSION_HUB__.inspect()
// → { version, sessions, uiSession, uiWorkspace, permission, statusRows, taskbarState,
// localUnread, focused, desktopShell, hostHalf, ... }
__DSH_SESSION_HUB__.getRows() // 当前状态栏行
__DSH_SESSION_HUB__.getTaskbarState() // red / yellow / green / none
__DSH_SESSION_HUB__.getLocalUnread() // { [sessionId]: seq }:补记的「后台完成、尚未查看」
__DSH_SESSION_HUB__.clearLocalUnread() // 手动清掉全部补记(绿灯随之熄灭)
__DSH_SESSION_HUB__.setTaskbarState('red') // 手动试任务栏红点(排障)
__DSH_SESSION_HUB__.setTaskbarState('none') // 清掉
__DSH_SESSION_HUB__.testNotification() // 弹一条测试通知,点它应跳回当前会话
__DSH_SESSION_HUB__.getNotifyLog() // 最近 20 条通知的落地结果(shown / failed / no-event / permission / deduped)
__DSH_SESSION_HUB__.clearNotifyLog() // 清空落地日志
__DSH_SESSION_HUB__.revealSessionRow('session-…') // 'session' / 'workspace' / null:验证左侧列表能滚到该会话行(不跳转)
__DSH_SESSION_HUB__.pingHost() // 'present' / 'absent' / 'unknown'
排障「窗口在不在前台」:把 DSH 切到后台,在控制台看 __DSH_SESSION_HUB__.inspect().focused ——它必须变成 false。如果它仍是 true,说明宿主的焦点判定与浏览器语义不一致,静默与绿点都会跟着错(这种情况请把 focused 的值和窗口状态一起反馈)。
宿主半只读诊断:
源(协议 + 主机 + 端口)跟着当前 DSH 实例走:页面是什么源,路由就在那个源上。
例如 Web 端在浏览器里打开 http://127.0.0.1:8779/ ,就用这个源;桌面端由 shell 同源转发。
$base = 'http://127.0.0.1:8779'
$h = @{ 'x-dsh-session-status-hub' = '1' } # 内部路由要求签名头,缺了会 403 Invoke-RestMethod -Uri "$base/dsh-session-status-hub/ping" -Method POST -Headers $h Invoke-RestMethod -Uri "$base/dsh-session-status-hub/debug" -Method POST -Headers $h 注意:签名头只用来挡跨站触发,不是鉴权(见第 11 节);桌面端页面内的 fetch 是同源通路。
手工验收:
1. 在 A 会话发一个长任务,切到 B 会话 / 别的应用 → 任务栏不点灯(运行中不亮),状态栏出现蓝色脉冲点「运行中」;
2. 任务完成后 → 弹「DSH · 任务完成」,状态栏出现绿点「待查看」,任务栏转绿,点 chip / 通知回到 A;
3. 当前对话本身发一个长任务,然后切到别的应用(窗口不置顶,但 A 仍是主视图)→ 任务跑完必须同时出现:系统通知「DSH · 任务完成」、任务栏图标绿点、状态栏「待查看」;切回窗口(且仍在看 A)后绿点与绿灯立刻熄灭;若改成点通知回来,也应当熄灭;验证 __DSH_SESSION_HUB__.inspect().focused 在切走时是 false、切回时是 true;
4. 让一轮失败 → 弹「DSH · 任务失败」,状态栏红点「报错」,任务栏变红(优先级高于黄/绿),把鼠标停在该会话行上,悬停卡片里出现红点「本轮失败」/「本轮中断」;
5. 触发提问 / 审批 → 弹「DSH · 需要你回应」,状态栏黄点「等待回答」,任务栏变黄;
6. 点击红点条目 → 报错确认,红点与任务栏红灯消失,行悬停提示同时消失。
7. 制造一个长列表(展开很多会话 / 把列表滚到远处),点击状态栏 chip 或通知跳转 → 左侧列表自动滚到目标会话行;目标所在分组折叠时,至少滚到它所属的分组标题。
9. 已知边界
- 宿主半的窗口状态修复需要重启一次 DSH:lib/raise-window.js 的 worker 脚本在宿主启动时加载,旧进程里仍是旧协议。POST /dsh-session-status-hub/ping 返回的 workerProtocol 为 2 才表示运行中的 worker 已保留最大化;客户端 chip 点击的修复不需要重启,刷新页面即可。
- 窗口前置依赖 Windows 前台锁:AttachThreadInput 组合在本机实测有效,但全屏应用、 UAC 提权窗口、远程桌面切换等极端场景仍可能拒绝;此时应用内跳转照常发生。
- 第一次点通知可能略慢:worker 需要先编译一次 C#(本机实测 5.9–9.6 秒,取决于杀软)。
通知一弹出就会预热;编译期间点击不会丢,指令排队、编译完立即执行。
- 任务栏点只在桌面端:Web 浏览器页面没有任务栏按钮,desktopShell=false 时不会上报。
- 「窗口在不在前台」用浏览器语义判定:document.visibilityState === "visible" 且
document.hasFocus()(Electron 的窗口失活会同时让 hasFocus() 变 false,被完全遮挡
时 visibilityState 可能变 hidden)。若上游换了外壳实现导致焦点语义漂移,通知静默与
绿点补记都会跟着错——__DSH_SESSION_HUB__.inspect().focused 是这条链路的体检点。
- 补记的「待查看」不会自动过期:与官方 completionUnread 同语义,需要你打开那个会话(或点它的通知 / chip)才熄灭;窗口回到前台并正在看它时会自动清掉。
- 安装前的历史冷会话不显示报错:投影检查点按 (sessionId, key) 落盘,本插件注册前结束的冷会话没有该键的行,需等该会话被打开一次(全量重折叠)才补齐。
- 报错不会自动过期:需要点击条目或打开该会话确认;这是刻意设计——失败值得你看一眼。
- includeSubagents 打开后,状态栏可能出现较多行,插件最多渲染 40 行。
- 左侧列表滚动依赖官方 DOM 约定:会话行 / 分组行按 data-row-key(session:<id> / workspace:<id>)定位。上游若改掉该属性,滚动会静默失效(跳转本身不受影响)——
__DSH_SESSION_HUB__.revealSessionRow(id) 返回 null 即可确认约定漂移。
10. 依赖与权限
- 零 npm 依赖:宿主半只用 node:child_process / node:fs;客户端半只用浏览器 API、 React(由客户端模块表提供)与 DSH 服务。
- 命名:仓库名、npm 包名、cordis.patch.yml 的 name、window.__ModuleLoader__ 的注册 id、 PLUGIN_ID(路由前缀 / localStorage 键 / 日志标签)统一为 dsh-session-status-hub;只有 Cordis 的节点 id(cordis.patch.yml 的 id)保持短名 session-hub,它只在 profile 树内部使用,不出现在任何对外命名里。
- 从 0.1.x 升级:插件 id 变了,localStorage 键前缀随之变化;客户端半启动时会把旧前缀下的配置 / 报错确认 / 补记搬一次(新键已有数据则不覆盖),搬完删掉旧键。
- 宿主半会起一个 PowerShell 进程(窗口前置 + 任务栏点),可用 raiseWindow: false 关闭窗口前置(任务栏点仍需要 worker);worker 空闲 10 分钟退出,状态点亮着时保持。
- 宿主半挂内部路由:POST /dsh-session-status-hub/{ping,debug,warmup,raise,taskbar}。
同源,且只接受 POST + 签名头(见第 11 节);只做「提窗口 / 画状态点 / 只读诊断」,不读任何用户数据。
11. 安全与隐私
- 没有任何外发网络请求:插件只跟本机宿主半(dsh-session-status-hub/ 同源路由)通信,没有遥测、没有统计、不访问第三方接口。
- 数据只落本机:配置、水位、报错确认、补记的「待查看」与通知落地日志全部存在浏览器 localStorage(键前缀 dsh-session-status-hub/);会话投影的检查点由 DSH 自己的投影注册表落盘。插件不读会话正文,只读「最后一个回合的结局」与几个状态布尔值。
- 内部路由要求签名头:客户端每条请求都带 x-dsh-session-status-hub: 1,宿主半见到才处理(缺头直接 403)。这不是鉴权,而是用来挡「跨站触发」:自定义头会把浏览器请求升级成需要预检的跨站请求,而宿主半不返回任何 CORS 响应头,于是别的网页即使能 POST 到本机端口也到不了 handler——/raise(把窗口弹到前台)与 /warmup(拉起 PowerShell worker)因此不会被任意网页反复触发。端点本身仍只影响窗口与任务栏图标,不返回用户数据(/debug 只回投影键、版本与进程状态)。即便如此,也不要把 DSH 的 Web 端口暴露到不可信网络。
- 签名头是本版新增:宿主半与客户端半需要同时是当前版本或更新;旧客户端半(不带该头)会被新宿主半拒绝,控制台会明确提示「缺签名头,刷新页面即可对齐」。
- 宿主半会起一个 PowerShell 进程(powershell.exe -NoProfile -EncodedCommand,不落脚本文件、不改执行策略)。worker 脚本全文见 lib/raise-window.js 的 WORKER_SCRIPT;不需要窗口前置时可用 raiseWindow: false 关掉。
License
MIT © 2026 railgun20001预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
railgun20001/dsh-session-status-hub
dsh-session-status-hub —— 会话状态中心状态栏 / Windows 通知 / 任务栏状态点
相关插件 · 市场 / 管理
插件精选列表awesome-dsh-plugin/awesome-dsh-plugin
DeepSeek Harness(dsh)插件精选列表
★ 22,399未汉化DSH
73.6
新词雷达zhuyansen/new-term-radar
new-term-radar新词雷达
★ 2,049已汉化MCP
72.8
插件生态导航0xsline/awesome-deepseek-harness
安装 贡献指南 DeepSeek Docs 公开插件目录 Issues
★ 1,188已汉化DSH
72.6
技能注册中心iflytek/skillhub
企业级开源智能体技能注册中心——发布、发现并管理组织内可复用的技能包。
★ 5,384已汉化DSH
71.9
插件市场目录imsai-sh/awesome-deepseek-harness-plugins
面向 DeepSeek Harnessdsh生态的社区插件目录,共收录 13950 个插件含 PR 收录与…
★ 360已汉化DSH
71.9
插件精选清单Anil-matcha/awesome-dsh-plugin
DeepSeek Harnessdsh精选指南——DeepSeek…
★ 1,013已汉化DSH
71.7
这个插件是你维护的吗?
作者本人可认领并核对本条目;认领通过后,详情页会显示「作者已认领」标记。