dsh-macos-notify:用 DSH 自身身份發 macOS 原生通知

前言

在 macOS 上給 DeepSeek Harness(DSH)加通知,社區裏常見的做法是開一個外部進程跑 osascript -e 'display notification ...'。這條路在新版 macOS 上有兩個實打實的問題:通知實際歸屬發起 osascript 的父進程,與 DSH 的簽名對不上,系統可能一聲不響把通知丟掉;就算顯示出來,圖標也不是 DSH,點擊也回不到會話。

dsh-macos-notify 換的思路是:讓 DSH 應用自己的 GUI 進程來發通知。插件由 Chanceux-L 維護,屬於客戶端類插件,通過 DSH 的雙半(宿主 + 客戶端)結構實現,目標是通知歸屬 DSH 自己的名字和圖標,並且點一下就能跳回對應會話。

架構

插件分宿主半邊和客戶端半邊,中間靠 Web 接口輪詢連接:

┌─ 宿主半邊 index.js(Node,inject: ['sessions','webServer'])──────────┐
│ 監聽頂級會話事件:turn/end(completed/error)、ask_user_question、   │
│ approval/asked                                                        │
│  → 通知寫入內存隊列(上限 100 條)                                    │
│  → ctx.webServer.register 暴露:                                      │
│      GET /macos-notify/now          → 當前最新通知 id                 │
│      GET /macos-notify/pending?since=N → 增量拉取隊列                 │
│ 子代理 / fork / team 成員會話帶 parentSession / delegationDepth 頭,  │
│ 直接跳過——只有用戶可見的頂級會話才通知。                             │
└──────────────────────────────────────────────────────────────────────┘
                              │ 輪詢(2 秒)
┌─ 客戶端半邊 client.js(Web GUI 頁面,inject: ['uiWorkspace'])───────┐
│ window.__ModuleLoader__.load 註冊,每 2s 拉取隊列                     │
│ → new Notification() 發出——Electron 渲染進程中即 DSH 應用自身的     │
│   macOS 原生通知                                                      │
│ → onclick 調 uiWorkspace.openSession(sessionId) 跳轉會話 +           │
│   window.focus()                                                      │
│ Notification 不可用/被拒時回退:頁內 AudioContext 提示音 + 標題閃爍  │
└──────────────────────────────────────────────────────────────────────┘
                              │ 頁面關閉(超過 clientQuietMs 無輪詢)
┌─ 宿主回退通道 ──────────────────────────────────────────────────────┐
│ afplay 播 /System/Library/Sounds 系統音效                             │
│ 阻塞輸入(提問/審批)時額外 open -a "DeepSeek Harness" 拉起窗口     │
└──────────────────────────────────────────────────────────────────────┘

宿主半邊負責監聽會話事件並維護隊列,客戶端半邊在渲染進程裏執行 new Notification(),這正是通知能歸屬 DSH 簽名的原因。子代理、fork 出來的會話和 team 成員會話會被跳過,只有用戶直接看到事件的頂級會話才通知。

核心功能

  • 回合完成 / 失敗通知:turn/end 時按 reason.kind 分派,失敗附錯誤消息(截斷至 120 字符),並顯示回合耗時。
  • 提問通知:Agent 調用 ask_user_question 時通知第一個問題(含 header),多個問題時顯示 (+N more)。
  • 審批通知:approval/asked 時通知工具名與原因。
  • 點擊跳轉會話:通知 onclick → uiWorkspace.openSession(sessionId) + window.focus()。
  • 無 GUI 回退:頁面關閉超過 clientQuietMs 未輪詢時,宿主直接 afplay 播系統音效;提問/審批還會 open -a 拉起 DSH 窗口。
  • 智能過濾:跳過子代理、fork、team 成員會話;短於 minTurnSeconds 的回合不通知;超過 10 分鐘的過期通知不補發。

全部行爲都可通過配置關閉或調整,見下文配置項。

安裝與啓用

推薦用 DSH CLI 以 github: 源碼形式安裝:

dsh plugin --profile web add github:Chanceux-L/dsh-macos-notify

也可以手動把目錄鏈接或克隆到 profile 的 node_modules 下:

git clone https://github.com/Chanceux-L/dsh-macos-notify \
  ~/.dsh/profiles/desktop/node_modules/dsh-macos-notify
# 或者開發時用符號鏈接:
ln -s /path/to/dsh-macos-notify ~/.dsh/profiles/desktop/node_modules/dsh-macos-notify

插件通過 cordis.patch.yml 以 insert 方式註冊(id: macos-notify),不需要手動改配置文件。重啓 DSH 後,第一次發通知會觸發系統的通知權限彈窗,允許即可。

配置項

在 profile patch 的 config: 下覆蓋,默認值定義在 index.js 的 DEFAULTS。

鍵 類型 默認 說明
onCompleted boolean true 回合完成時通知
onError boolean true 回合失敗時通知
onQuestion boolean true Agent 提問時通知
onApproval boolean true 需要工具審批時通知
minTurnSeconds number 5 回合短於該秒數不通知
titlePrefix string "DSH" 通知標題前綴,格式 前綴 · 會話標題
soundCompleted string "Glass" 宿主回退音效(/System/Library/Sounds 下的文件名,不帶 .aiff;'' 關閉)
soundQuestion string "Ping" 提問回退音效
soundApproval string "Funk" 審批迴退音效
soundError string "Basso" 失敗回退音效
activateOnInput boolean true 無 GUI 且提問/審批阻塞時 open -a 拉起 DSH 窗口
clientQuietMs number 15000 客戶端超過該毫秒未輪詢,宿主啓用回退通道
debug boolean false 追加投遞診斷到 ~/.dsh/macos-notify.log

配置示例:

- insert:
    - id: macos-notify
      name: 'dsh-macos-notify'
      config:
        titlePrefix: DSH
        minTurnSeconds: 10
        soundCompleted: Glass

典型用法

按上面的命令裝好並重啓 DSH 後,最小可用配置就是默認值,不需要額外寫 patch。下面給出常見調整的示例。

想讓快速連續的回合別刷屏,把閾值抬高:

- insert:
    - id: macos-notify
      config:
        minTurnSeconds: 30

頁面常駐、不需要宿主回退音效時,可以關掉對應開關:

- insert:
    - id: macos-notify
      config:
        onCompleted: true
        onError: true
        onQuestion: true
        onApproval: true
        clientQuietMs: 600000
        activateOnInput: false

排障時打開 debug,投遞診斷會追加到 ~/.dsh/macos-notify.log:

- insert:
    - id: macos-notify
      config:
        debug: true

與現有插件對比

dsh-macos-notify(本插件) CrombastiC/dsh-macos-notify jypjypjypjyp/dsh-notifier dsh-complete-notify
通知通道 應用自身 GUI 進程(Web Notification) 外部 osascript 進程 外部 osascript 進程 外部 osascript 進程
macOS 簽名校驗 通過,歸屬 DSH 自身 可能被靜默丟棄 可能被靜默丟棄 可能被靜默丟棄
通知圖標歸屬 DeepSeek Harness 其他應用身份 其他應用身份 其他應用身份
點擊跳轉會話 ✅ uiWorkspace.openSession ❌ ❌ ❌
頁面關閉回退 ✅ afplay + open -a 部分 部分 部分
提問/審批/失敗分類通知 ✅ 部分 部分 部分

適用場景與注意

適合在 macOS 上日常用 DSH 圖形界面、希望回合結束、Agent 提問或需要審批時收到可點擊通知的開發者。插件本身只做通知投遞,不改變會話邏輯。

幾點需要注意:

  • 插件以當前 dsh 進程的權限運行,安裝前應先檢查源碼與許可證(本倉庫爲 MIT)。
  • 插件依賴 Electron 渲染進程裏的 Web Notification API,需要 macOS 允許 DSH 發通知;首次觸發會彈系統權限框。
  • Dock 圖標數字角標需要 DSH 官方在 Electron 主進程暴露 app.dock.setBadgeCount IPC,插件層無法實現。
  • 跨工作區會話跳轉尚未實現,uiWorkspace.openSession 當前只支持當前工作區內的導航。
  • 宿主回退音效依賴 /System/Library/Sounds 下的系統音效文件。

小結

dsh-macos-notify 解決的問題很具體:讓 DSH 的通知在 macOS 上真正歸屬於應用自己,並且能點回會話。它用雙半插件結構把事件監聽、隊列和原生通知串起來,配套了過濾、回退和全套配置。如果需要在 macOS 上給 DSH 加通知,這是一個可以直接落地的方案。

目錄頁:https://www.skillhub.cn/plugins/Chanceux-L/dsh-macos-notify

GitHub:https://github.com/Chanceux-L/dsh-macos-notify

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜