Voiceオーバーレイライフサイクル(macOS)

対象読者:macOSアプリの開発者。目的:ウェイクワードとプッシュトゥトークが重なったときにVoiceオーバーレイの動作を予測可能に保つこと。

現在の意図

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

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

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

次のステップ

  1. VoiceSessionCoordinator(アクター)
    • 同時に1つのVoiceSessionのみを保持。
    • API(トークンベース):beginWakeCapturebeginPushToTalkupdatePartialendCapturecancelapplyCooldown
    • 古いトークンを持つコールバックをドロップ(古い認識器によるオーバーレイの再表示を防止)。
  2. VoiceSession(モデル)
    • フィールド:tokensource(wakeWord|pushToTalk)、committed/volatileテキスト、チャイムフラグ、タイマー(自動送信、アイドル)、overlayMode(display|editing|sending)、クールダウン期限。
  3. オーバーレイバインディング
    • VoiceSessionPublisherObservableObject)がアクティブセッションをSwiftUIにミラーリング。
    • VoiceWakeOverlayViewはパブリッシャー経由でのみレンダリング。グローバルシングルトンを直接変更しない。
    • オーバーレイのユーザーアクション(sendNowdismissedit)はセッショントークンを使ってコーディネータにコールバック。
  4. 統一された送信パス
    • endCapture時:トリミングされたテキストが空 → 閉じる、そうでなければperformSend(session:)(送信チャイムを1回再生、転送、閉じる)。
    • プッシュトゥトーク:遅延なし。ウェイクワード:自動送信のオプション遅延。
    • プッシュトゥトーク終了後、ウェイクランタイムに短いクールダウンを適用して即座の再トリガーを防止。
  5. ログ
    • コーディネータがサブシステムai.openclaw、カテゴリvoicewake.overlayおよびvoicewake.chime.infoログを出力。
    • 主要イベント:session_startedadopted_by_push_to_talkpartialfinalizedsenddismisscancelcooldown

デバッグチェックリスト

  • オーバーレイが固着する問題を再現しながらログをストリーム:

    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. セッション引き継ぎ、クールダウン、空テキスト閉じるの統合テストを追加。