dsh-notify
Manifest validDSH Notification Display Layer Plugin: A global toast stack that subscribes to the desktop shell's `__dshExo` bridge events; clicks programmatically activate sessions through the official sessions runtime. When no shell is present, it degrades to subscribing to the sessions store and renders itself. The pluggable web side of the notification display layer.
dsh-notify
DSH 通知显示层插件(可插拔通知显示层的 web 侧)
全局右上角 toast 栈,订阅桌面壳 __dshExo 桥事件(对话完成 / 服务事件 / 更新就绪),点击经官方 dsh-client-runtime 的 sessions store 程序化激活会话(替代壳侧 __reactFiber$ DOM hack);无壳时自动降级订阅 sessions store 自绘轮次完成 toast。
面向 DSH-Exoskeleton 设计文档
docs/NOTIFICATION-PLUGIN-DESIGN.md§6 实现。壳核分离不破坏:检测逻辑(zstd watcher / 服务健康 / 更新)留在主进程,本插件只负责显示与交互。
1. 解决什么问题
| 用户问题 | 本插件对应的解法 |
|---|---|
| 通知不弹 / 漏报 | 桌面壳把事件经 webview 预加载桥(__dshExo)投递给页面内插件 → auto 决策「webview 在线优先」,dev/portable 无 System Toast 时通知仍然在页面内可见 |
| 点击跳错会话 / 跳不过去 | 插件在页面内直接用官方 @deepseek-ai/dsh-client-runtime/client 的 sessions store 程序化选中/打开会话(会话 ID 精确),不再从壳外猜 DOM、不依赖 __reactFiber$ |
| 每轮对话都通知,太吵 | 显示层策略化:聚合窗口、每轮/聚合粒度由壳侧 hub 决定后下发(本插件只渲染);无壳降级时同会话连发原位刷新而非堆积,避免覆盖层通知风暴 |
2. 架构(两种数据源,运行时互斥)
┌─ 壳桥模式(默认,覆盖式首选)────────────────────────────────────┐
│ DSH-Exoskeleton 主进程 notification-hub(壳侧,P1) │
│ └─ webviewProvider(P2:WebContentsView 预加载桥 __dshExo) │
│ │ onEvent(ev) send() │
│ ▼ ▲ │
│ ┌─ dsh web 页面 ─────────────────────────────────────────────┐ │
│ │ dsh-notify 插件(本包) │ │
│ │ · 检测到 window.__dshExo → ready() 握手 → 订阅 onEvent │ │
│ │ · 渲染 toast 栈(按 kind 分级) │ │
│ │ · 点击 → ctx.sessions.open(id) 程序化激活 │ │
│ │ → __dshExo.send('notify:click', …) │ │
│ └────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
┌─ 无壳降级(浏览器直开 dsh web,无 __dshExo)───────────────────┐
│ dsh-notify 插件 │
│ · 订阅 ctx.sessions.list,检测 completed 0→1 边缘(= 侧边栏 │
│ 绿色 "done":运行中→完成且未选中) │
│ · 自绘「对话完成」toast;点击 → ctx.sessions.open(id) │
│ · 页面内闭环,无 OS 通知 │
└────────────────────────────────────────────────────────────────┘
- 事件是"事实",显示是"策略":本插件只渲染壳/列表喂给它的完成事件,聚合/渠道等策略由壳侧 hub 决策。
- 桥与降级互斥(有桥绝不再开 sessions 降级),天然无双通道重复。
3. 桥契约(window.__dshExo)
壳侧 P2 已落地(
src/preload/dsh-view.ts+window-manager.attachDshView挂 preload +notification-hubwebview 通道),本契约即当前实现,供壳侧后续维护对齐。
插件只消费最小白名单 API,不依赖任何任意 IPC:
// 壳侧 → 页面(预加载桥 window.__dshExo,经 contextBridge 暴露)
interface DshExoBridge {
/** 订阅壳推送的通知事件,返回取消函数 */
onEvent(cb: (ev: NotificationEvent) => void): () => void
/** 页面 → 壳:点击 / 已读回执 */
send(channel: 'notify:click' | 'notify:seen', payload: { id: string; sessionId?: string }): void
/** 页面 → 壳:握手(插件就绪后才投递,防事件丢失) */
ready(): void
/** 壳版本探针(仅用于日志) */
appInfo(): { version: string }
}
// 通知事件(与设计 §3.1 一致)
type NotificationEventKind =
| 'session-done' | 'service-ready' | 'service-error' | 'service-restarting' | 'update-ready'
interface NotificationEvent {
id: string
kind: NotificationEventKind
title: string
body: string
ts: number
session?: { sessionDir: string; workspace: string; uuid: string; file: string; turn?: number; project?: string; sessionTitle?: string; firstUserText?: string }
service?: { port?: number; error?: string; restartCount?: number }
update?: { version?: string }
actions?: { onClick?: () => void } // 原生 provider 专用;webview 通道序列化后无回调
}
桥侧交互时序(壳实现参考):
壳 webviewProvider 建桥(preload:sandbox+contextIsolation 暴露 __dshExo)
→ 等页面 __dshExo.ready() 握手
→ 握手后「webview 在线」,auto 决策投向 webview provider
→ view.webContents.send('dsh-notify:event', ev) → preload 转发给 onEvent 订阅者
→ 收到 notify:click → windowManager.show()(会话激活由插件侧 ctx.sessions.open 完成)
→ 收到 notify:seen → hub 记录已读回执(R-26)
未握手不投递(防漏报);握手丢失 → native 兜底。
4. 安装
从包源(npm / GitHub,推荐 —— 任意机器可装)
# npm 源(发布后)
dsh plugin --profile web add dsh-notify
# GitHub 源
dsh plugin --profile web add github:qgx1992/dsh-notify
桌面壳内也可用「插件」面板安装:目录会按 dsh-plugin topic / npm keywords 自动发现,一键安装。
已知坑(AGENT.md §7.1):新发布 <24h 时 pnpm 供应链策略可能报
ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION,把该版本加进 profile 的minimumReleaseAgeExclude白名单即可。
本地开发装法(改源码调试用)
# 因项目路径含空格+中文,link: 直连会在 dsh→pnpm 转发链里被拆坏;
# 用无空格 junction 指向真实目录(改动实时生效,路径移动后需重建 junction)
New-Item -ItemType Junction -Path "$env:USERPROFILE\dsh-notify-link" `
-Target "D:/A-my project/研究agent桌面端/DSH-Exoskeleton/plugins/dsh-notify"
dsh plugin --profile web add link:C:/Users/QIU/dsh-notify-link
装完后:dsh web 重启生效(客户端 bundle 在启动时组合,仅刷页面不生效),
页面刷新即加载 /plugins/dsh-notify/client.js。
5. 行为细节
- toast 栈:右上角,全局。按 kind 分级:对话完成=信息、服务就绪=成功、服务异常=错误、服务重启=警告、更新就绪=信息(左侧竖色条 + DSW 主题变量,自动适配亮/暗色)。
- 自动关闭:信息 8s / 成功 6s / 错误与更新 15s / 警告 12s;× 手动关闭;同屏上限 5 条(超出淘汰最旧)。
- 对话完成 toast:
项目「X」· 标题(壳下发 body 原样渲染;无壳降级由插件按cwd项目名 +displayTitle组装)。点击 →ctx.sessions.open(sessionId)程序化激活 +notify:click回传。 - 激活可靠性与防御:
open()前先校验会话 id 是否在列表(unknown id会 fail loud,避免炸进插件);列表瞬态(phase=pending)下每 500ms 重试至多 4 次;再点可按需复用byId精确 id。 - 已读回执:事件渲染即
notify:seen(壳侧 hub 用以上报投递成功/去重)。 - i18n:独立 locale NS
dsh-notify(zh/en)。data 前缀:data-dsh-notify-*。 - DOM 纪律:toast 栈是插件自建的
fixed覆盖容器,绝不搬动 DSH slot 渲染出的节点(搬节点 ↔ 框架重渲染互相触发会导致渲染进程 100% CPU 卡死,见 AGENT.md 已知坑);卸载插件(fiber dispose)整体拆除样式 + 根容器 + 全部 toast。 - 静默降级:所有异常 try/catch,不向壳/页面抛。
6. 设计验证结论(对应设计文档 §11「待验证清单」)
| # | 待验证项 | 结论(本插件取证) |
|---|---|---|
| 1 | dsh-client-runtime 的 sessions store 是否暴露程序化选中/打开会话的 API | ✅ 存在。ISessions.open(id: SessionId): void("Select a session as current"),实现 SessionRuntime.open;ctx.sessions 可见(inject "sessions" 后)。§6.3 首选路径可行 |
| 2 | 页面是否容忍注入 __dshExo 全局 | 插件侧只消费,不注入;__dshExo 命名冲突检测由壳侧 preload 负责(R-27,页面无同名全局时才注入) |
| 4 | WebContentsView + sandbox + preload + contextBridge 兼容性 | 壳侧 P2 阶段验证;本插件按标准 contextBridge 契约实现,无特殊前提 |
| — | 覆盖式 toast 渲染安全性 | ✅ 与生态先例(dsh-pet)同型:自建 position: fixed 容器 + <style>,不搬 slot 节点 |
无壳降级的"轮次完成"信号:SessionSummary.completed(侧边栏绿色 done 标记,
SessionManager 在 running→idle 且未选中时 arming)。勘察结果见
@deepseek-ai/dsh-client-runtime/lib/types/client/contract/sessions.d.ts 与
sessions/service.d.ts。
7. 与 DSH-Exoskeleton 壳侧协作(P0–P2 已落地,P3/P4 约定)
壳侧已完成(本次同步交付,npm run typecheck / npm test(113 项)通过):
- P0 ✅:AUMID 对齐(
index.tssetAppUserModelId('io.dsh.exoskeleton'),与electron-builder.ymlappId 一致)。 - P1 ✅:
src/main/notification-hub.ts(dispatch/ 聚合策略 / Provider 路由 / 投递回执日志 R-26),原notify.ts降为 native 内部实现。 - P2 ✅:
src/preload/dsh-view.ts预加载桥(window.__dshExo白名单,R-27)+window-manager.attachDshView挂 preload + hub webview 通道(在线判定 = view attach && 收到ready()握手)+ 三处事件源(session-watcher / index 服务事件 / updater)改走 hub。
约定:webview 通道下,壳收到 notify:click 只需 windowManager.show(),会话激活由插件 ctx.sessions.open() 完成 —— 这是替换 activateSessionInWebUi DOM hack 的关键收益;壳侧 activateSessionInWebUi 保留为 native 通道 / 页面异常时的兜底(actions.onClick 携带)。
P3 / P4(未做,按需后续):把 dsh-notify 加入 RECOMMENDED_PLUGINS 默认预置/推荐;管理面板「设置」页渠道自检 + 通知渠道/粒度/聚合窗口 UI(现状:notifyChannel 默认 auto 已生效,粒度默认 per-turn 保持现状零回归,聚合窗口默认 20000ms,均可在 config.json 手动调整)。
8. 目录结构
plugins/dsh-notify/
├── package.json # dsh.client.platform=web;dsh.client.inject 留空(不静态注入源码上下文)
├── cordis.patch.yml # bundle 挂载声明(insert 进 DSH 配置树)
├── lib/
│ ├── index.js # 宿主(node 侧)入口:纯浏览器插件,无宿主行为
│ └── client.js # 浏览器 bundle:toast 栈 + 桥订阅 + 程序化激活 + 无壳降级 + i18n
├── README.md # 本文档
└── LICENSE # MIT
9. 开发 / 发布
- 改
lib/client.js后:node --check lib/client.js语法校验 →node test/smoke.cjs冒烟测试(在无浏览器环境用最小 DOM shim 真实执行apply(),覆盖:bundle 导出形状 / 无壳降级 completed 边缘弹 toast / 程序化激活 / 更新就绪 notify:install / 同事件去重 / 卸载拆除)→ 本地link:重装 → 重启 dsh web 实测。 - 当前默认本地分发:不推 GitHub、不
npm publish。版本语义化(改代码后 bump),README/description 维护好即可。 - 平台标记:
dsh.client.platform = "web";exports['./client']供模块系统组合/plugins/dsh-notify/client.js。
Versions
| Latest version | Published | Size |
|---|---|---|
| 0.1.1 | — | — |
| 0.1.2 | — | — |
| 0.1.3 | — | — |
| 0.1.4 | — | — |
| 0.1.5 | — | — |
| 0.1.6 | — | — |
| 0.1.7 | — | — |
Comments
Loading…
From the same category
DeepSeek Harness plugin for Reactive Resume: bridges your resumes and job applications into a Harness session over MCP.
★ 41.7k
↓ 270/wk
MIT
Aug 24, 2026
dsh plugin --profile web add dsh-plugin-reactive-resumeby Tencent
Let AI agents use your real, logged-in browser without interrupting your work. CLI + extension for browser automation across any shell-capable AI agent.
★ 8.1k
↓ 7k/wk
MIT
TypeScript
Sep 30, 2026
dsh plugin --profile terminal add @wxg-prc-cpg/browser-skill-dsh-pluginby yjh051108
dsh-routing-suite — injector + router-standard kit: install the runtime injector first, then the task-aware reasoning-mode router preset (measured P1-P23).
★ 7k
MIT
JavaScript
Sep 18, 2026
dsh plugin --profile web add @dsh-external/dsh-super-injectorby Q00
Agent OS: the agent gets smarter on its own. We just hold the line: Interview-gated, staged evaluation, budgeted evolution loop. MCP server, 14 runtimes: Claude Code, Codex CLI, Gemini CLI, OpenCode,
★ 6.2k
MIT
Python
Oct 4, 2026
by dsh-market
The plugin market inside DeepSeek Harness — browse, search, one-click install · DSH 可视化插件市场
★ 5.5k
↓ 164.4k/wk
MIT
TypeScript
Oct 3, 2026
dsh plugin --profile web add dshmarketby superdesigndev
OpenRouter for agent tools. Join community here: https://discord.gg/6mQYYfFMAn
★ 4.1k
NOASSERTION
Python
Oct 4, 2026
dsh plugin --profile web add treg-dsh