# dsh-notify [English](../README.md) | [简体中文](README.zh-CN.md) | [日本語](README.ja.md) **dsh(DeepSeek Harness)向けプラグイン。dsh がユーザーの注意を必要とするとき、デスクトップ通知でお知らせします。** dsh 自体は「ユーザーの介入が必要」な場面で何の合図も出しません。権限確認(承認)待ち、 `ask_user_question` による質問、プラン(計画)の承認待ち、ターン/セッション完了、 ゴールの完了・ブロック、エラー発生、ワークフロー終了 —— ターミナルやブラウザのタブが バックグラウンドにあると、こうしたタイミングを見逃しがちです。本プラグインは dsh ホストの イベントバスを監視し、これらの瞬間にネイティブのデスクトップ通知(macOS / Linux のシステム通知、 Windows のポップアップ、ターミナルベル、または独自コマンド)を即座に表示します。 本プラグインは**純粋なオブザーバー**です。すべてのリスナーは受動的で、特に `approval/request` リスナーは通知するだけで、ユーザーに代わって判断することは**決してなく**、 常に `next()` でリクエストを転送します。承認チェーンの動作は本プラグイン未導入時と完全に同じです。 ## 機能 | トリガー | 発火タイミング | デフォルト | |---|---|---| | `approval` | 権限確認リクエスト(サンドボックス昇格、承認が必要な操作など) | オン | | `question` | `ask_user_question` による質問 / `exit_plan_mode` によるプラン承認待ち | オン | | `turnComplete` | ターン完了(エージェントの応答が終わり、次の入力を待っている状態) | オン | | `goal` | ゴールの完了 / ブロック | オン | | `error` | セッションのエラー発生 | オン | | `workflow` | `tool_workflow` の実行終了 | オン | 通知チャンネル(`channel`): - `auto`(デフォルト): macOS → `osascript` システム通知 / Windows → PowerShell ポップアップ / その他 → `notify-send` - `osascript` / `notify-send` / `powershell`: チャンネルを明示指定 - `bell`: ターミナルベル(`\x07`)— 依存ゼロのフォールバック - `custom`: 独自コマンドテンプレートを実行(プレースホルダー `{title}` `{body}` `{app}`) - `none`: 無効 ランタイム依存は `@deepseek-ai` の 2 パッケージのみ(settings と schemastery)。任意の profile(`web` / `headless` / カスタム)で動作します。 すべてのオプションは Web の **設定 → 通知** タブからも変更できます(後述の「設定タブ(Web)」参照)。 変更は設定ファイルに書き込まれ、即座に反映されます(再起動不要)。 ## インストール `web` profile を例に説明します(他の profile も同様): ```sh # 1. プラグインを profile の依存関係に追加します。 # ローカルディレクトリは pnpm が node_modules にリンクします。git URL や # 公開済みの npm パッケージ名でも構いません。 dsh plugin --profile web add /path/to/dsh-notify # または本リポジトリから: dsh plugin --profile web add git+https://github.com/knownothing114/dsh-notify.git # または(npm 公開後): dsh plugin --profile web add dsh-notify ``` このコマンドは依存関係の追加と**バンドル登録を自動で行います**。profile マニフェストの `dsh.profile.bundles` に `dsh-notify` が含まれているか確認し、含まれていない場合は手動で追加してください: ```jsonc // ~/.dsh/profiles/web/package.json { "dsh": { "profile": { "bundles": [ "@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app", "dsh-notify" // ← この行を追加 ] } } } ``` ```sh # 2. dsh web を再起動します。プラグインは起動時に読み込まれます(HMR は設定のみホットリロード)。 ``` ## 設定 初期状態で妥当なデフォルト値が組み込まれているため、通常は変更不要です。調整したい場合は、 profile の `cordis.patch.yml`(または `--patch` オーバーレイ)で行 id `dsh-notify` を指定して 上書きします。上書きは該当行の `config` を**丸ごと置き換える**ため、プラグイン側でデフォルトと 深いマージを行います。変更したいキーだけを書けば OK です: ```yaml # ~/.dsh/profiles/web/cordis.patch.yml - id: dsh-notify config: channel: auto # auto | osascript | notify-send | powershell | bell | custom | none appName: dsh # 通知に表示される送信元名(custom テンプレートの {app}) sound: true # macOS で通知音を鳴らす minIntervalMs: 3000 # 通知間の最小間隔(連投ガード) rootsOnly: true # ルートセッションのみ通知(サブエージェント / バックグラウンド子タスクを無視) verbose: false # 各通知を dsh ログにも出力 customCommand: "" # channel が custom のときのコマンドテンプレート(例): # terminal-notifier -message {body} -title {title} triggers: approval: true question: true turnComplete: true goal: true error: true workflow: true enabled: true # マスタースイッチ ``` ## 設定タブ(Web の「通知」タブ) `web` profile では、プラグインが **設定** に専用の **「通知」** タブを登録します。上記の 設定項目をすべて表示し、フォームから変更できます(保存 / 破棄 / 全体の「初期値に戻す」): - マスタースイッチ、通知チャンネル、アプリ名、通知音、最小通知間隔、ルートセッションのみ、ログ出力、カスタムコマンド - 6 つのトリガースイッチ 設定の優先順位(高い方が有効): **設定ファイル**(`$DSH_HOME/settings.yaml`。タブが書き込み、 ホット適用)→ **profile パッチ**(`cordis.patch.yml` の `config:`)→ 組み込みデフォルト値。 ホストプラグインは通知のたびに設定をリアルタイムで読み取るため、タブでの変更は即座に反映されます。 ブラウザ側は `dist/client.js`(dsh クライアントモジュール形式のプリビルドバンドル。ホストが `/plugins/dsh-notify/client.js` として配信)で実装され、ホスト側が `notify` 設定名前空間を 登録します。プラグインパッケージの初回インストール・アップグレード時は `dsh web` の再起動が 必要です。以後のタブ内の変更に再起動は不要です。 注記: dsh の API プロキシはデフォルトで**ホワイトリストに載った**設定名前空間だけを Web UI に 公開します(モデルプロバイダー + 明示的な許可リスト)。そのため本プラグインはプロキシの settings ハンドラをラップし、`notify` 名前空間を標準の settings RPC で読み書きできるように します。他の名前空間はコアのホワイトリスト動作のままです。 ## 仕組み(イベントマッピング) | トリガー | 監視するイベント | |---|---| | `approval` | ホスト側の `approval/request` ウォーターフォール(受動的: `return next()` で転送し、判定には関与しません) | | `question` / プラン承認 | `session/event` 内の `tool/call`(ツール名 `ask_user_question` / `exit_plan_mode`)。質問文はツール引数から抽出 | | `turnComplete` | `session/event` 内の `turn/end`(`reason.kind === "completed"`) | | `goal` | `goal/changed`(操作 `complete` / `block`)。本文にゴールの内容を含む | | `error` | `agent/error` | | `workflow` | `session/event` 内の `tool-workflow/run-end`(`completed` 以外の停止理由でも通知) | すべてのリスナーはルートコンテキストに登録されます。dsh のスコープルーティング規則により すべてのエージェント / セッションイベントを受信でき、`rootsOnly` は `ctx.agents.roots()` で サブエージェントのノイズを除外します。通知は分離された `spawn` プロセスで送信されるため、 エージェントループをブロックすることはなく、失敗はログに記録されるだけです。 ## アンインストール ```sh dsh plugin --profile web remove dsh-notify # さらに ~/.dsh/profiles/web/package.json の bundles から "dsh-notify" を削除し、再起動 ``` ## よくある質問 - **通知が出ない?** 解決された `channel` が OS と合っているか確認してください。macOS なら `channel: osascript` を明示(`verbose: true` で dsh ログに各通知が出力されます)。 Linux では `notify-send`(`libnotify`)が必要です。おやすみモードや通知権限も確認してください。 - **通知が多すぎる?** `minIntervalMs` を増やすか、不要な `triggers` をオフにしてください。 - **サブエージェントがノイズになる?** `rootsOnly: true` のままにしてください。 - **設定変更が反映されない?** `cordis.patch.yml` の変更は HMR でホットリロードされます。 プラグインの追加・削除や `package.json` の bundles 変更は `dsh web` の再起動が必要です。 ## 開発 ```sh cd dsh-notify npm install # @deepseek-ai/dsh-settings と @deepseek-ai/schemastery をインストール npm test # node --test: 純関数のユニットテスト + apply() のスモークテスト + クライアントフォームテスト(30 ケース) ``` ## プロジェクト構成 ``` ├── lib/index.mjs ホストプラグイン(イベントリスナー、設定名前空間、apiProxy 公開ラッパー) ├── dist/client.js ブラウザバンドル(設定 → 通知 タブ)。/plugins/dsh-notify/client.js として配信 ├── cordis.patch.yml bundle パッチ(プラグイン行のマウント) ├── docs/ │ ├── README.zh-CN.md 简体中文文档 │ ├── README.ja.md 日本語ドキュメント │ └── third-party-settings-namespace-exposure.md 開発メモ: サードパーティ設定名前空間の Web UI への公開方法 ├── test/ node --test テストスイート(ユニット / レンダー / インタラクション / 統合) └── package.json プラグインマニフェスト(exports、dsh.bundle / dsh.client 宣言) ``` ## ライセンス [MIT](../LICENSE)