Voice Overlay 라이프사이클 (macOS)
대상은 macOS app contributor입니다. 목표는 wake-word와 push-to-talk가 겹칠 때도 voice overlay 동작을 예측 가능하게 유지하는 것입니다.현재 의도
- overlay가 이미 wake-word 때문에 떠 있는 상태에서 사용자가 hotkey를 누르면, hotkey session은 텍스트를 reset하지 않고 기존 텍스트를 인계받습니다. hotkey를 누르는 동안 overlay는 계속 유지됩니다. 키를 놓았을 때 trimmed text가 있으면 전송하고, 없으면 닫습니다.
- wake-word만 사용 중일 때는 silence 이후 자동 전송이 계속 동작하고, push-to-talk는 key release 시 즉시 전송됩니다.
구현됨 (2025-12-09)
- overlay session은 각 capture(wake-word 또는 push-to-talk)마다 token을 가집니다. token이 일치하지 않으면
partial/final/send/dismiss/levelupdate를 버려 stale callback을 막습니다. - push-to-talk는 현재 보이는 overlay text를 prefix로 인계받습니다. 즉, wake overlay가 떠 있는 상태에서 hotkey를 누르면 기존 text를 유지하고 새 음성을 뒤에 붙입니다. final transcript는 최대 1.5초까지 기다리고, 없으면 현재 text로 fallback합니다.
- chime/overlay logging은
voicewake.overlay,voicewake.ptt,voicewake.chimecategory에서info레벨로 출력됩니다. session start, partial, final, send, dismiss, chime reason이 포함됩니다.
다음 단계
- VoiceSessionCoordinator (actor)
- 한 번에 정확히 하나의
VoiceSession만 소유 - API(token 기반):
beginWakeCapture,beginPushToTalk,updatePartial,endCapture,cancel,applyCooldown - 오래된 token을 가진 callback을 버려, stale recognizer가 overlay를 다시 열지 못하게 함
- 한 번에 정확히 하나의
- VoiceSession (모델)
- field:
token,source(wakeWord|pushToTalk), committed/volatile text, chime flags, timer(auto-send,idle),overlayMode(display|editing|sending), cooldown deadline
- field:
- Overlay binding
VoiceSessionPublisher(ObservableObject)가 active session을 SwiftUI에 반영VoiceWakeOverlayView는 publisher를 통해서만 렌더링하며, global singleton을 직접 수정하지 않음- overlay user action(
sendNow,dismiss,edit)은 session token과 함께 coordinator로 되돌림
- 통합 전송 경로
endCapture시 trimmed text가 비어 있으면 dismiss, 아니면performSend(session:)호출(send chime 1회, forward, dismiss)- push-to-talk는 지연 없음, wake-word는 auto-send용 선택적 지연 허용
- push-to-talk 종료 후 wake runtime에 짧은 cooldown을 적용해 wake-word가 즉시 다시 트리거되지 않게 함
- 로깅
- coordinator는 subsystem
ai.openclaw, categoryvoicewake.overlay와voicewake.chime에.infolog를 출력 - 핵심 event:
session_started,adopted_by_push_to_talk,partial,finalized,send,dismiss,cancel,cooldown
- coordinator는 subsystem
디버깅 체크리스트
-
sticky overlay를 재현하면서 log를 stream합니다:
- active session token이 하나뿐인지 확인합니다. stale callback은 coordinator에 의해 버려져야 합니다.
-
push-to-talk release가 항상 active token으로
endCapture를 호출하는지 확인합니다. text가 비어 있으면 chime이나 send 없이dismiss가 나와야 합니다.
마이그레이션 단계(권장)
VoiceSessionCoordinator,VoiceSession,VoiceSessionPublisher추가VoiceWakeRuntime이VoiceWakeOverlayController를 직접 건드리지 않고 session을 생성/업데이트/종료하도록 refactorVoicePushToTalk가 기존 session을 인계받고 release 시endCapture를 호출하도록 refactor, runtime cooldown 적용VoiceWakeOverlayController를 publisher에 연결하고 runtime/PTT의 직접 호출 제거- session 인계, cooldown, empty-text dismiss에 대한 integration test 추가