CH 22 · UI プラグイン:dsh のスキンを変更し、パネルを追加し、コンテンツを差し込む
本章のゴール
前章までのプラグインは「見えない」場所で動作していました:ツール登録、呼び出しの遮断、依存関係の提供。本章では見える層に切り替えます——プラグインは Web UI の見た目と機能を直接変更できます。本章では UI プラグインが何ができるか、どう動くかを明確にし、ハンズオンで本物のイベントストリームを観察します。
あなたはすでに UI プラグインを使っている
CH 17 を振り返ると、3 つのプラグインをインストールしました。すべて UI プラグインです:
| プラグイン | 役割 |
|---|---|
| dsh-theme | Web UI のスキンを変更(設定 → 外観にテーマカードが追加) |
| dsh-oil-sticky-prompt | スクロール時に最新のユーザーメッセージを上部にピン留め |
| dsh-better-sidebar | サイドバーワークベンチ、ファイルパネルから直接ディレクトリを表示 |
当時は単に「プラグインをインストールした」だけだったかもしれませんが、今や理解できます:これらはすべて UI を変更している。これが UI プラグインへの第一層の理解——「見える」カスタマイズのすべてを担当しています。
UI プラグインができること
おおよそ 3 つのカテゴリの機能があります:
- スキン変更:色・フォント・レイアウトスタイルの変更。最も軽いカテゴリで、dsh-theme がこれに該当します。
- レイアウト変更 / パネル追加 / 機能追加:サイドバー・トップバー・入力欄を動かすほか、完全な機能モジュールや可視化パネルをインターフェースに直接追加できます。dsh-better-sidebar、dsh-oil-sticky-prompt がこのカテゴリ;CH 12 でインストールした DSH Skill & MCP Panel もこれに該当——設定に「MCP 管理」エントリを直接追加し、各サーバーの状態とツール数が一目でわかるようになり、Web UI に可視化運用パネルを追加したのと同じです。
- 会話へのコンテンツ提供:セッション内にカスタム行をレンダリング(例えば特定のツール結果をカード化、特殊ノードの挿入)。最も深いカテゴリで、公式には「Web Client に業務行を提供」と称されています。
最初の 2 カテゴリは多くが純粋なフロントエンド作業;3 番目は本当に「会話レンダリングパイプラインに組み込む」必要があります。
UI プラグインの動作原理:すべてはイベントストリームから始まる
UI プラグインはページ上の DOM を直接操作せず、イベントストリームを購読します。UI プラグインを理解するうえで最も重要なポイントです。
セッション全体を通じて、すべての出来事は標準イベントになります:ユーザーがメッセージを送信、モデルがストリーミング出力を開始、あるステップが開始、特定のツール呼び出しが発生……これらのイベントは水のように session/event チャネルを流れます。組み込みのインターフェースはこれらから会話バブル・軌跡・ステータスをレンダリング;UI プラグインも同じで、ctx.on('session/event', ...) を通じて同じストリームを購読します。
公式の UI プラグイン最小例はまさしくこれを行います:
export const name = 'my-ui'
export const inject = ['agents']
export function apply(ctx) {
ctx.on('session/event', (_session, event) => {
if (event.type === 'assistant/chunk' && event.data.chunk.type === 'text-delta') {
console.log(event.data.chunk.text) // モデルストリーミング出力の各チャンク
}
})
}メカニズム全体の骨格:
あなたが入力し、モデルが返信し、セッションコントローラがこれらをすべてイベントストリームに翻訳します;UI プラグインと組み込みインターフェースは同じストリームを購読し、各々が必要なものを取得します。インターフェースは「イベントストリーム」の消費者であり、DOM の直接操作者ではない——これは「すべてはプラグイン」が UI さえもカバーできる理由を説明します:インターフェースでさえ、プラグインがイベントストリームから「聞き出した」ものです。
ハンズオン:「イベントオブザーバー」をインストールしてイベントストリームを観察する
メカニズムを見るだけでは物足りないので、すべてのセッションイベントを出力するミニマルなプラグインを書きましょう——一回の会話の裏で実際にどれだけの出来事が起きているかを見ます。
ステップ 1:ディレクトリ作成、プラグイン作成
プラグインを配置したいワークスペースで:
New-Item -ItemType Directory -Path "ui-demo\src" -Forceui-demo\src\event-watch.js を作成:
export const name = 'event-watch'
export function apply(ctx) {
ctx.on('session/event', (_session, event) => {
console.log(`[event] ${event.type}${event.data?.type ? ' / ' + event.data.type : ''}`)
})
}session/event を購読して、各イベントのタイプを出力するだけです。
ステップ 2:プラグインを宣言する
ui-demo\cordis.yml を作成(パスは自分のものに置き換え、スペースは %20 に変換):
- insert:
- id: event-watch
name: 'file:///E:/your-workspace/ui-demo/src/event-watch.js'ステップ 3:headless で一度実行
cd 自分のワークスペースディレクトリ
dsh --profile headless --patch "./ui-demo/cordis.yml" "ただ「hi」と返答してください"実際にターミナルに出力された内容:

「hi」と答えるだけで、裏側では 22 個のイベントが走りました:セッション初期化(permission、sandbox、approval)、turn/start、step/start、3 回の user/message、リクエストヘッダ、assistant/chunk の一文字ずつのストリーミング出力、assistant/message の確定、step/end、turn/end……
Web UI で見ているあの会話は、まさにこの一連のイベントが消費されてレンダリングされたものです。イベントオブザーバーによって、UI の背後にある原料を初めて「見る」ことができました。
dsh に任せる:見える UI プラグインを開発する
前述のイベントオブザーバーは単に「イベントを見る」だけで、実際にはインターフェースを変えていません。dsh に直接「設定ページで効果が見える」UI プラグインを開発させれば、それもできます。
Web UI の入力欄にこのプロンプトを直接送ってください:
現在のワークスペースで、Web UI で見える簡単な UI プラグインを開発してください:設定ページにカスタムタブを一つ追加します(例えば「マイプラグイン」と名付け、ページに何かプレースホルダーコンテンツを置くだけで、複雑な機能は不要です)。まず dsh 公式ドキュメントを読んで UI プラグインのメカニズムと設定ページへのタブ追加方法を理解してから、公式仕様に従って実装し、どのプロファイルにインストールするか、インターフェースでどう見えるか(dsh の再起動や設定変更が必要かも併せて)を教えてください。dsh は自分でドキュメントを読み、技術スタックを判断し(こうしたインターフェースプラグインは通常 TypeScript とフロントエンドビルドが必要で、dsh が自分で処理します)、実装し、検証方法を教えてくれます。その指示に従って dsh を再起動すると、設定ページに新しいタブが現れます。
これは実際に一度走らせた例です——ドキュメントを読むことからプラグインを書くこと、プロファイルにインストールすること、インターフェースでの確認方法を伝えること、終了方法まで、すべて自力でこなし、重要なパスまで強調してくれました:

再起動後、設定 → プラグインを開くと、上部に「マイプラグイン」という新しいタブが追加されています:

細かいポイント:このプラグインは純粋な UI で、Host 側の振る舞いはないため、settings.yaml / cordis.yml を一切変更しません——すべて profile の bundles 経由でロードされます。ロールバックもシンプル:bundles 配列から削除するだけです。これも「すべてはプラグイン」の体現例:インターフェースにタブを一つ追加することさえ「プラグインをインストールする」ことなのです。
よくある落とし穴
| 問題 | 何が起きているか | 対処方法 |
|---|---|---|
| イベントオブザーバーが出力しない | session/event の購読が誤っている、またはプラグインがロードされていない | cordis.yml のパスと ctx.on の記述を確認 |
| イベントが多すぎて読めない | 一回の会話で本来数十個のイベントがある | まず turn/start から turn/end の間を見る、そこが主干 |
| 会話内に直接コンテンツを描画したい | React renderer が必要で、上級者向け | まずイベントストリームと設定項目に慣れ、上級は後で |
Cannot find package が出る | プラグインが外部依存を使っているがインストールされていない | プラグインディレクトリで対応パッケージを npm install |
この章で学んだこと
- [ ] UI プラグインの 3 つの機能カテゴリを説明できる:スキン変更 / レイアウトとパネル追加 / セッションコンテンツ提供
- [ ] 「インターフェースはイベントストリームの消費者である」という中核メカニズムを理解している
- [ ]
ctx.on('session/event', ...)を使ってセッションイベントを購読できる - [ ] イベントオブザーバーを使って一回の会話の背景にあるイベントストリームを見られる
- [ ] UI プラグインで設定ページにカスタムタブを追加できることを知っている
