macOSコンパニオンアプリ

音声オーバーレイ

対象読者: macOSアプリコントリビューター。目標: ウェイクワードとプッシュトゥトークが重複した際も、音声オーバーレイの動作を予測可能に保つ。

現在の意図

  • オーバーレイがウェイクワードですでに表示されている状態でユーザーがホットキーを押した場合、ホットキーセッションはテキストをリセットせず、既存のテキストを引き継ぎます。オーバーレイはホットキーが押されている間表示されたままです。ユーザーがキーを離したとき: トリミングされたテキストがあれば送信、なければオーバーレイを閉じます。
  • ウェイクワード単独の場合、無音時に自動送信されます。プッシュトゥトークはキーを離した瞬間に即時送信します。

実装済み (2025年12月9日)

  • オーバーレイセッションは、キャプチャ(ウェイクワードまたはプッシュトゥトーク)ごとにトークンを持つようになりました。トークンが一致しない場合、部分/最終/送信/閉じる/レベル更新のコールバックは破棄され、古いコールバックによる影響を回避します。
  • プッシュトゥトークは、表示されているオーバーレイのテキストを接頭辞として引き継ぎます(ウェイクオーバーレイが表示されている間にホットキーを押すと、テキストが保持され、新しい音声が追加されます)。最終トランスクリプトを最大1.5秒待ち、それがない場合は現在のテキストにフォールバックします。
  • チャイム/オーバーレイのログは、カテゴリ voicewake.overlayvoicewake.pttvoicewake.chimeinfo レベルで出力されます(セッション開始、部分、最終、送信、閉じる、チャイム理由)。

次のステップ

  1. VoiceSessionCoordinator (アクター)
    • 常に1つの VoiceSession のみを所有します。
    • API (トークンベース): beginWakeCapture, beginPushToTalk, updatePartial, endCapture, cancel, applyCooldown
    • 古いトークンを含むコールバックを破棄します(古い認識エンジンがオーバーレイを再び開くのを防ぎます)。
  2. VoiceSession (モデル)
    • フィールド: token, source (wakeWord|pushToTalk), 確定/揮発性テキスト, チャイムフラグ, タイマー (自動送信, アイドル), overlayMode (表示|編集|送信中), クールダウン期限。
  3. オーバーレイバインディング
    • VoiceSessionPublisher (ObservableObject) は、アクティブなセッションをSwiftUIに反映します。
    • VoiceWakeOverlayView はパブリッシャー経由でのみレンダリングし、グローバルシングルトンを直接変更することはありません。
    • オーバーレイでのユーザー操作 (sendNow, dismiss, edit) は、セッショントークンとともにコーディネーターにコールバックします。
  4. 統一された送信パス
    • endCapture 時: トリミングされたテキストが空 → 閉じる。それ以外 → performSend(session:) (送信チャイムを一度再生し、転送し、閉じます)。
    • プッシュトゥトーク: 遅延なし。ウェイクワード: 自動送信のためのオプションの遅延。
    • プッシュトゥトーク終了後、ウェイクランタイムに短いクールダウンを適用し、ウェイクワードが即座に再トリガーされないようにします。
  5. ロギング
    • コーディネーターは、サブシステム ai.openclaw、カテゴリ voicewake.overlay および voicewake.chime.info ログを出力します。
    • 主要イベント: session_started, adopted_by_push_to_talk, partial, finalized, send, dismiss, cancel, cooldown

デバッグチェックリスト

  • オーバーレイが固着するケースを再現しながらログをストリーミング:

    コピー

    sudo log stream --predicate 'subsystem == "ai.openclaw" AND category CONTAINS "voicewake"' --level info --style compact
    
  • アクティブなセッショントークンが1つのみであることを確認します。古いコールバックはコーディネーターによって破棄されるべきです。

  • プッシュトゥトークのキー離しで、常にアクティブなトークンとともに endCapture が呼ばれることを確認します。テキストが空の場合、チャイムや送信なしで dismiss されることを期待します。

移行手順 (提案)

  1. VoiceSessionCoordinatorVoiceSessionVoiceSessionPublisher を追加します。
  2. VoiceWakeRuntime をリファクタリングし、VoiceWakeOverlayController を直接操作する代わりに、セッションの作成/更新/終了を行うようにします。
  3. VoicePushToTalk をリファクタリングし、既存のセッションを引き継ぎ、キー離しで endCapture を呼び出すようにします。ランタイムクールダウンを適用します。
  4. VoiceWakeOverlayController をパブリッシャーに接続します。ランタイム/PTTからの直接呼び出しを削除します。
  5. セッション引き継ぎ、クールダウン、空テキスト時の閉じる動作の統合テストを追加します。

Voice WakeWebChat