dsh
OpenPetsHQ/openpets将 DeepSeek Harness 的生命周期状态、错误与审批请求,桥接到本地运行的 OpenPets 桌面伙伴。
回合事件通知:完成/出错/被中断/等待审批/AI 提问/达到上限六类事件,经声音、系统弹窗、页内提示、webhook、IM、宿主桌面通知通道推送,多窗口只响一次。
DeepSeek Harness 消息通知插件:AI 回合产生六类事件(完成/出错/被中断/等待审批/AI 提问/达到上限),按你的配置送达声音、系统弹窗、页内提示、webhook、IM、宿主桌面通知六种通道,多窗口只响一次。
dsh plugin --profile web add @mzzsfy/dsh-turn-notify安装后打开 设置 > 消息通知:默认全部事件开启,声音与系统弹窗即装即用(系统弹窗经一次浏览器授权后生效);webhook、IM 与宿主桌面通知填配置后启用。页内提示与推送总线由公共依赖 @mzzsfy/dsh-common-tools 提供,由 session-manager 插件代挂装载;总线为 client 硬依赖,订阅 WS 总线 @mzzsfy/dsh-turn-notify 插件通道。host/client 在 apply(ctx) 调用 setupCommonTools(ctx),通道身份取 ctx.fiber.entry.options.name 的完整 npm 包名;client 各实例退出时释放自己的订阅,同一 factory 的存活实例接管一次共享通知工作,最后实例退出才中止共享快照并清启动状态;跨 factory HMR 清理旧代。声音通道受浏览器自动播放策略约束,首次点击页面后才会出声。想推到微信等聊天工具,见 IM 推送;想推到自建服务,见 webhook 推送;想在宿主机桌面弹系统通知(浏览器全关也送达),见 宿主桌面通知。
插件监听 AI 会话,把回合结果归入六类,每类可独立开关、独立指定声音:
事件 · 含义 任务完成 · AI 回合正常结束 任务出错 · AI 回合以错误结束,或被你手动停止 被中断 · 会话关闭时未正常收尾的回合被补发中断结束(少见) 等待审批 · AI 请求工具授权,等你点击允许;回合以等待审批结束也归此类 AI 提问 · AI 调用 ask_user_question 向你提问 达到上限 · 回合因输出长度上限结束
过滤条件(全部可在面板调整):
声音通知是本插件的核心能力,全部在浏览器内完成,不依赖系统音效与音频文件:
声音由抢到呈现权的窗口播放,多个窗口打开同一页面时只有一个窗口出声(见 多窗口只响一次)。
等待你动作的审批与提问、以及回合结束类通知,呈现时都携带直达动作:点击页内提示卡片或系统弹窗,窗口聚焦并自动切换到对应会话(模拟点击侧边栏会话行,与手工点击完全同路),高亮随之清除(查看即已读)。会话行未渲染(列表折叠/懒加载未滚动到)时仅聚焦窗口;通知无会话标题时同样只聚焦。「知道了」类显式关闭不触发直达。
审批请求与 AI 提问送达后 10 分钟仍未处理时,自动补发一轮提醒(页内提示 + 声音 + 标题闪烁,独立于聚焦静默与通道路由——它就是"上一轮没看到"的兜底),每条通知至多补发一次,不连环催。"已处理"的判定:你已切到该会话,或已点掉会话行高亮;通知无会话标题时无法感知处理,按未处理补发。通知被其他窗口处理或已移出投影队列时,补发登记自动撤销。
回合结束类通知附一句话小结:耗时(回合时长)+ tokens(本回合各步用量四桶合计)+ 最终回答前缀(末条含文本回答的首行,按码点截断),拼装为「耗时 12 秒 · 1.5k tokens · 已修复登录」形态;缺哪项省哪项,全缺省无小结。系统弹窗作正文、页内卡片作第二行,webhook payload 增补 summary 与 tokens 结构化字段。统计来自 host 侧会话事件流内的 assistant 消息,回合结束即消费清账,无额外请求。
六类事件各自可指定放行通道名单(kindRoutes,存 settings.yaml),名单外的通道对该类事件一律不送达;未配置的分类全通道放行,名单为空数组即全禁。通道名:sound(提示音)/ system(系统弹窗)/ toast(页内提示,页内提示音随行)/ blink(降级标题闪烁)/ webhook / im / host(宿主桌面通知)。典型用法:任务完成只推 webhook,重要事件(审批/提问)走 IM:
turn-notify:
kindRoutes:
completed: [webhook] # 完成只推 webhook,浏览器不响
approval: [im, sound, system] # 审批走 IM 且本机弹窗出声路由是全局配置,作用于所有浏览器;本机呈现偏好(声音/弹窗开关、聚焦静默)在其上叠加,两层都放行才送达。面板"通知"分区不展示该配置,yaml 直改保存即生效。
在面板填入 webhook URL 即启用。host 直发,标签页全关也送达;payload 为 Slack 兼容 JSON(text 字段承载通知文本,summary 字段承载通知小结),另附 event/category/status/session/workspace/durationMs/tokens/ts 结构化字段;超时 10 秒,不重试。URL 原文随配置响应回显、保存即提交输入框内容(清空并保存即禁用),测试按钮返回真实投递结果。
开启面板"通知"页的宿主通知开关后,通知触发时由宿主进程直接弹操作系统级桌面通知,零依赖:macOS 走 osascript,Linux 走 notify-send,Windows 走 PowerShell 调 WinRT toast;超时 10 秒,失败即弃不重试,不影响其余通道。去重方向为浏览器优先:浏览器在场(公共 WS 总线的 @mzzsfy/dsh-turn-notify 插件通道有连接,或最近活动仍在 2 秒认可窗口内)时由浏览器呈现,宿主让位,正常路径零重复;浏览器离场(标签页全关)时宿主补位。重复只发生在在场判定的边界情况(认可窗口边界与网络抖动),宁可重复不可漏。两个开关判定一致,任一开启即生效:
"浏览器在场"由公共 WS 总线 @mzzsfy/dsh-turn-notify 插件通道的连接计数记账:该空间有连接即在场,连接关闭即时出账,最近活动在 2 秒认可窗口内仍算在场;异常断连的清理由公共总线处理。来源不限地址:浏览器可在任意机器访问 dsh(局域网部署),在场信号看的是"有无页面订阅本插件空间",与浏览器同不同机无关。浏览器在场但在场浏览器的呈现偏好全关(声音/弹窗/页内全关或系统通知未授权)时,宿主同样让位,该事件在浏览器侧无 OS 级通知(仅剩会话行高亮)。宿主在无桌面的服务器上时 spawn 会静默失败,无害;测试页"测试宿主通知"返回真实结果,可据此验证宿主机桌面环境是否可用。聚焦时宿主通知照弹(宿主无法感知窗口焦点,聚焦静默是浏览器侧偏好)。
安装 @xmanrui/dsh-im 后自动启用,可推送到微信等九种渠道。投递目标的新建与平台测试在 dsh-im 设置页完成,本插件只做选择:从已绑 bot 的目标目录勾选即保存,支持绑定多个 bot。IM 卡顶部有总开关,关闭后不向已配目标投递、目标绑定原样保留,临时停用不必逐个移除目标,再开即恢复。触发逻辑与 webhook 一致,fire-and-forget 不重试;bot 离线时该次通知弃置不补发。测试页的"测试 IM 通知"为显式点火,不受总开关影响。
通知实际送达时,侧边栏会话列表中对应会话行以背景脉冲闪烁强调,六类各一色(完成绿/出错红/提问蓝/中断橙/审批黄/上限紫),点击该会话行即停止。开关在面板"偏好"页首项(独立开关,本机生效,各浏览器独立),默认开启。并行多会话时用于快速定位状态刚更新的会话:运行中的会话不闪烁(与 dsh 原生状态点分工——状态点表达运行中,闪烁表达已更新待查看),会话重新进入运行状态时闪烁自动让位;聚焦时正在查看的会话不闪烁,失焦期间本会话的通知照常闪烁,返回窗口即自动消失(回来即已读,焦点与页面可见双通道)。行定位按会话标题文本匹配,标题默认与侧边栏行同源(session-title 投影);该服务缺失或不可用(如伪会话无事件日志)时回落会话事件内存储的标题,可能与侧边栏不同步。会话行未渲染(列表折叠/懒加载未滚动到)时该次高亮静默跳过。重开页面的冷启动静默对齐同样挂高亮(见"多窗口只响一次"),重开即见哪些会话有动静。
侧边栏会话列表按工作区分组(官方 WorkspaceBrowser 分组模式)时,有运行中会话的工作区文件夹组头显示脉冲运行点并给文件夹图标着色,游离(未归属任何工作区)的可见运行中会话使末位「未分组」组头同样标记——折叠与展开同样生效。归属与运行态一律取服务快照投影,DOM 仅按下标对位官方组容器;组容器数漂移(单列表模式/搜索态/结构变更)即清除标记不误标。图标着色让位官方 folderActive(展开且含当前会话)态。开关在面板"偏好"页"会话列表"卡(存宿主配置,各浏览器共用),默认开启,变更刷新页面生效。
同一地址(同源)打开的多个窗口共享一次呈现:各窗口订阅公共 WS 总线 @mzzsfy/dsh-turn-notify 插件通道(首连、重连与变更触发通知队列全量快照拉取,空闲期无周期请求),经 IndexedDB 原子事务认领,本机偏好与提醒标记保留在 localStorage,仅一个窗口抢先呈现——发声并弹出页内提示、系统弹窗、高亮会话行,其余窗口全部跳过,不止声音。失效广播与快照刷新各自按一秒窗口合并,持续变更时最终快照会收敛;不同浏览器或不同地址打开的窗口各自发声互不感知。窗口关闭/刷新期间错过的通知不做重开回放:启动首轮静默对齐游标与完成标记,不逐条补发提示与声音(等待用户动作的审批与提问例外,照常呈现);未读会话行高亮不在静默范围,照常挂上。
认领记录自认领起按浏览器本地时间保留 90 秒(60 秒投影窗口 + 30 秒请求消费窗口),不按单窗旧快照删除。快照读取与后续事务认领共用超时信号;处理超时或实例卸载会取消尚未提交的认领,其他窗口可接手。通知时间戳保持宿主口径,不要求与浏览器时钟同步。IndexedDB 被阻止或不可用时会通告降级,此时仅保证本窗口去重。
设置 > 消息通知,五个分区:
分区 · 内容 通知 · webhook 地址、最短回合时长、子代理过滤、宿主通知(总开关与回退)、六类事件开关;存 settings.yaml,事件开关点击即存,其余点保存后生效。事件→通道路由(kindRoutes)亦存此命名空间,经 yaml 配置 偏好 · 本机浏览器记忆的设置:会话高亮(独立开关)、声音总开关与分类静音、音量、系统弹窗与授权、页内提示开关、页内提示音(总开关 + 分类独立开关)、聚焦静默、降级标题闪烁 音效 · 音效上传/试听/重命名/删除,六类事件的音效映射与双作用域开关,页内提示音映射(本机,未配置沿用通知音效) IM · IM 总开关(关闭保留目标)、绑定 dsh-im bot、勾选投递目标(仅装了 dsh-im 时显示) 测试 · 声音/页内/系统/宿主/webhook/IM 逐通道点火,回执即真实结果
面板与 settings.yaml 读写同一命名空间,两边改动互通、保存即生效。webhookUrl 随面板配置响应回显,yaml 直改同样可见。
turn-notify:
webhookUrl: '' # webhook 目标 URL,留空禁用
minTurnDurationMs: 5000 # 最短回合时长(毫秒),回合结束类通知短于此不送达
rootsOnly: true # 子代理会话不通知
suppressSubagentWake: true # 子代理相关回合不通知:后台委托未收尾或收尾唤醒(仅任务完成类)
hostNotify: false # 宿主机桌面通知总开关;浏览器优先,本插件空间有 WS 连接时让位
hostNotifyFallback: false # 宿主通知补位:浏览器离场且超出认可窗口时弹宿主桌面通知,与总开关判定一致
enabled: # 六类事件独立开关
completed: true
error: true
interrupted: true
approval: true
ask: true
max-tokens: true
soundMapping: # 每类事件音效映射,空为内置默认,值为内置音名或上传音效 id
completed: ''
kindRoutes: # 事件→通道路由:分类到放行通道名单,未配置的分类全通道放行
completed: [webhook]
imTargets: # dsh-im 投递目标,空数组禁用
- botId: wx_xxx
targetId: owner
imEnabled: true # IM 推送总开关:关闭后不投递,目标绑定保留音量、聚焦静默、分类静音等本机偏好存浏览器 localStorage,不经 yaml。
按数据流向分五步:
session/event(回合结束、ask_user_question 工具调用与 assistant 消息)、approval/asked 审批事件(审批通知唯一信号:生产者先 append 后发 waterfall,会话事件流恒先达且不依赖应答方放行)。回合结束原因逐一映射:正常结束归任务完成,错误与手动停止归任务出错,审批拦截归等待审批,输出触顶归达到上限,会话收尾补发的中断归被中断;assistant 消息流经途累积回合小结(tokens 与回答前缀),回合结束随通知消费。cd packages/dsh-turn-notify && npm testnode scripts/dev-link.mjs dsh-turn-notify # 仓库根执行:归一 profile 依赖行 + 挂 junction工作副本以 junction 挂进 profile,host 半区改动保存约 1 秒热重载,client 半区改动刷新页面即生效,无需发版;规约与全仓归一见 node scripts/dev-link.mjs all。
按仓库动态窗口验证(scripts/compat/window.mjs),不预置任何固定版本槽:主测 = 渠道 dist-tag 最新 rc,完全适配口径;基线 = npm latest,不崩溃底线口径;主测与基线同版时收敛单槽。兼容面以窗口实测为准,不逐版本承诺。逐轮实测结论见 docs/兼容性测试/逐包测试进度.md 与本包 兼容性测试.md。