FURYU Tech Blog - フリュー株式会社

フリュー株式会社の開発者が技術情報を発信するブログです。

AIエージェントの通知は、どうやってターミナルに届いているのか

※この記事には部分的に生成AIが利用されています。人間が文章全体を書いたのち、生成AIによるテクニカルライティング観点でのレビューを受け、人間が指摘内容を反映したものです。

はじめに

こんにちは、フリューのソフトウェアエンジニアkitajimaです。ピクトリンクの開発を担当しています。

僕は現在Ghosttyというターミナルを使っています。

ghostty.org

Ghosttyの画面。dotfilesの解説を依頼している様子
使い始めた理由は「設定ファイルでカスタムできそう」「キーバインドでペインを分割しやすい」「あとかっこいい」

Claude Codeの作業が終わったり許可待ちになったりすると、Ghosttyからデスクトップ通知が届きます。作業中はペインの上端に進捗バーが出て、タブのタイトルも変わります。特に何も設定した覚えがないので、「なんか"いい感じに"やってくれるな〜」くらいの認識でありがたく使っていました。

最近になってこの裏側を調べてみたところ、OSCという仕組みが使われていることを初めて知りました。本記事では、AIエージェントが出した通知がターミナルに届くまでの流れと、そこで使われているOSCについてご紹介します。

通知が届くまでの経路

エージェントの状態が人間に届くまでの経路は、大まかに次のようになっています。

AIエージェント
  │  完了 / 入力待ち / 失敗
  ▼
hooks・設定・組み込み機能
  │  OSC / OS API / 外部コマンド
  ▼
ターミナル / OS
  │  デスクトップ通知・音・表示の変化
  ▼
人間(視覚、聴覚、触覚)

エージェントが状態の変化を何らかの手段でターミナルやOSに伝え、それがデスクトップ通知や音、表示の変化として人間に届きます。その手段の1つがOSCです。

OSCとは

OSC(Operating System Command)は、ターミナルへの命令を標準出力に文字列として書き込む仕組みです。エスケープシーケンスの一種で、ESC ] 番号 ; 引数 BELという形をしています。

$ printf '\033]9;ビルドが終わりました\007'

上記コマンドを、OSC 9をサポートしているターミナルで実行すると、デスクトップ通知が出ます。普通の文字列として標準出力に書いているだけなので、シェルスクリプトからでも、エージェントのようなプログラムからでも、同じように使えます。

OSCは通知専用の仕組みではなく、次のようにターミナルへの命令全般に使われています。

番号 用途
OSC 0/2 ウィンドウ・タブのタイトル変更
OSC 7 作業ディレクトリの通知
OSC 8 ハイパーリンク
OSC 9 デスクトップ通知

冒頭で「タブのタイトルも変わる」と書きましたが、これもOSC 0/2によるものでした。

Ghosttyの画面。コマンドを実行してタブのタイトルが変更された状態
OSC 2 によりタブのタイトルを変えられたGhostty

通知と進捗に使われるOSC

通知や進捗の表示に使われている方式には、以下のようなものがあります。

方式 できること
OSC 9 本文だけのデスクトップ通知
OSC 777 タイトル+本文の通知
OSC 99 通知の更新、ボタン、クリックで元のタブへ復帰(kitty公式ドキュメント)
OSC 9;4 進行中、エラー、一時停止、など進捗の表示

同じデスクトップ通知でも、製品によって対応している番号や、できることが微妙に違います。

エージェント側は何をしているか

Claude Codeは、動いているターミナルを判定して、製品に合わせたOSCを出し分けています。ここでのポイントは次の4つです。

  • Claude Code公式ドキュメントより、通知の自動判定の対象は、執筆時点でGhostty、kitty、iTerm2
  • 設定リファレンスより、進捗バー(OSC 9;4)の対象は、ConEmu、Ghostty 1.2以降、iTerm2 3.6.6以降
  • 判定外のターミナルには、既定ではOSCは送られない
    • ただし設定preferredNotifChannel"kitty""ghostty"に固定すれば、その製品向けのOSCを送るようになる
  • 通知系のOSCを解釈できない製品でも、preferredNotifChannel"terminal_bell"に切り替えたり、hooksに通知スクリプトを置いたりすれば、任意の手段で通知を受け取れる

つまり、Claude Codeは製品単位で判定しますが、通知が届くかどうかは同じOSCでやり取りできるかで決まります。この違いを、自動判定される製品とされない製品で実際に確かめてみました。

例: 自動判定されるGhostty

僕が何も設定せずに通知を受け取れていたのは、Ghosttyが自動判定の対象だったからでした。Ghosttyが解釈できる方法の一つであるOSC 777をClaude Codeが出してくれるわけです。(preferredNotifChannel"auto"である必要あり)。

通知

OSC 777によるデスクトップ通知が次のように届きます。

Ghosttyの通知
通知はGhostty名義で出る

進捗

Ghostty公式ドキュメントにあるOSC 9;4による進捗が、ペインの上端にバーとして表示されます。進行中は青、一時停止はオレンジ、エラーは赤と、色で状態を区別できます。

Ghosttyの画面。4ペインにそれぞれ進捗(50%、一時停止、進行中、エラー)の状態が表示されている
注意: これはprintfで出したもので、Claude Code経由ではない

例: 自動判定されないOtty

Ottyというターミナル製品を比較に用います。

OttyはOSC 9/99/777を解釈できます。

docs.otty.sh

しかし、Claude Codeのドキュメントにある自動判定の対象一覧に名前はありません。

通知

まず、preferredNotifChannel"auto"のままでは通知が届きませんでした。

次に、preferredNotifChannel"kitty""iterm2"に固定すると、通知が届くようになりました。それぞれOSC 99、OSC 9が使われたと推測できます。 Claude Codeが製品名を知らなくても、同じOSCを解釈できれば通知は受け取れることが確認できました。

Ottyの通知
`preferredNotifChannel`="kitty"の状態ではOttyの通知が来た

進捗

Otty公式ドキュメントより、AIエージェントが発信したOSC 9;4を解釈できること、バーティカルタブに状態を表示できることが確認できます。

howは変わっても、whatはしばらく残りそう

求めていること(what)は「離れていてもエージェントの状態が自分に届く」ことです。2026年現在の実現手段(how)の1つが、OSC 9/99/777による通知、OSC 9;4による進捗表示、hooksにあたります。

OSC自体は規格化されていますが、番号ごとの意味は製品ごとに独自に定めています。対応状況もまちまちです。エージェント側の対応も変わり続けているので、ここに書いた具体的な手段は、1年後には違うものになっているかもしれません。

一方で、「状態が届いてほしい」というwhat自体は、エージェントに作業を任せて待つという使い方が続く限り、しばらく変わらないのではないかと思っています。新しいターミナルやエージェントが出てきたときも、「状態が自分に届くか、どう届くか」を見れば、乗り換えるべきかを判断しやすくなりそうです。

さいごに

今回、AIエージェントの通知がターミナルに届く仕組みが分かりました。これにより、今後登場する製品についても「この製品はOSCを使っているんだな」「いよいよ新しいプロトコルが採用されたんだな」といったふうに、根拠を持ってターミナル選定ができそうだと感じました。

みなさんが利用しているターミナルには、どのような方法で通知が届いているでしょうか?