음성 오버레이 라이프사이클 (macOS)
대상: macOS 앱 기여자. 목표: wake 단어와 push-to-talk이 겹칠 때 음성 오버레이를 예측 가능하게 유지합니다.
현재 의도
- wake 단어로 인해 오버레이가 이미 표시된 상태에서 사용자가 핫키를 누르면 핫키 세션이 텍스트를 초기화하는 대신 기존 텍스트를 채택합니다. 오버레이는 핫키가 눌려 있는 동안 유지됩니다. 사용자가 해제하면: 트리밍된 텍스트가 있으면 전송하고, 없으면 닫습니다.
- Wake 단어만 있는 경우 여전히 침묵 시 자동 전송됩니다. Push-to-talk은 해제 시 즉시 전송됩니다.
구현됨 (2025년 12월 9일)
- 오버레이 세션은 이제 캡처 (wake 단어 또는 push-to-talk)당 토큰을 가집니다. 토큰이 일치하지 않으면 부분/최종/전송/닫기/레벨 업데이트가 삭제되어 오래된 콜백을 방지합니다.
- Push-to-talk은 접두사로 표시되는 오버레이 텍스트를 채택합니다 (핫키를 누르는 동안 wake 오버레이가 올라가 있으면 텍스트를 유지하고 새 음성을 추가). 최종 전사를 위해 최대 1.5초 기다린 후 현재 텍스트로 대체됩니다.
- 차임/오버레이 로깅은 카테고리
voicewake.overlay,voicewake.ptt,voicewake.chime에서info로 내보내집니다 (세션 시작, 부분, 최종, 전송, 닫기, 차임 이유).
다음 단계
- VoiceSessionCoordinator (actor)
- 한 번에 정확히 하나의
VoiceSession을 소유합니다. - API (토큰 기반):
beginWakeCapture,beginPushToTalk,updatePartial,endCapture,cancel,applyCooldown. - 오래된 토큰을 가진 콜백을 삭제합니다 (이전 인식기가 오버레이를 다시 열지 못하도록 방지).
- 한 번에 정확히 하나의
- VoiceSession (model)
- 필드:
token,source(wakeWord|pushToTalk), 커밋된/휘발성 텍스트, 차임 플래그, 타이머 (자동 전송, 유휴),overlayMode(display|editing|sending), 쿨다운 기한.
- 필드:
- 오버레이 바인딩
VoiceSessionPublisher(ObservableObject)가 활성 세션을 SwiftUI에 미러링합니다.VoiceWakeOverlayView는 게시자를 통해서만 렌더링합니다. 글로벌 싱글톤을 직접 변경하지 않습니다.- 오버레이 사용자 작업 (
sendNow,dismiss,edit)은 세션 토큰으로 코디네이터에 콜백합니다.
- 통합 전송 경로
endCapture시: 트리밍된 텍스트가 비어 있으면 → 닫기; 그렇지 않으면performSend(session:)(전송 차임 한 번 재생, 전달, 닫기).- Push-to-talk: 지연 없음; wake 단어: 자동 전송을 위한 선택적 지연.
- Push-to-talk이 완료된 후 wake 런타임에 짧은 쿨다운을 적용하여 wake 단어가 즉시 재트리거되지 않도록 합니다.
- 로깅
- 코디네이터는 서브시스템
ai.openclaw, 카테고리voicewake.overlay및voicewake.chime에서.info로그를 내보냅니다. - 주요 이벤트:
session_started,adopted_by_push_to_talk,partial,finalized,send,dismiss,cancel,cooldown.
- 코디네이터는 서브시스템
디버깅 체크리스트
고착된 오버레이를 재현하는 동안 로그를 스트리밍합니다:
bashsudo log stream --predicate 'subsystem == "ai.openclaw" AND category CONTAINS "voicewake"' --level info --style compact활성 세션 토큰이 하나만 있는지 확인합니다. 오래된 콜백은 코디네이터에서 삭제되어야 합니다.
Push-to-talk 해제가 항상 활성 토큰으로
endCapture를 호출하는지 확인합니다. 텍스트가 비어 있으면 차임이나 전송 없이dismiss가 예상됩니다.
마이그레이션 단계 (제안)
VoiceSessionCoordinator,VoiceSession,VoiceSessionPublisher를 추가합니다.VoiceWakeRuntime을VoiceWakeOverlayController를 직접 건드리는 대신 세션을 생성/업데이트/종료하도록 리팩토링합니다.VoicePushToTalk을 기존 세션을 채택하고 해제 시endCapture를 호출하도록 리팩토링합니다. 런타임 쿨다운을 적용합니다.VoiceWakeOverlayController를 게시자에 연결합니다. 런타임/PTT에서 직접 호출을 제거합니다.- 세션 채택, 쿨다운, 빈 텍스트 닫기에 대한 통합 테스트를 추가합니다.