Ciclo di vita dell’overlay vocale (macOS)
Pubblico: collaboratori dell’app macOS. Obiettivo: mantenere prevedibile l’overlay vocale quando wake-word e push-to-talk si sovrappongono.Intento attuale
- Se l’overlay è già visibile a causa della wake-word e l’utente preme il tasto rapido, la sessione del tasto rapido adotta il testo esistente invece di reimpostarlo. L’overlay rimane visibile finché il tasto rapido è premuto. Quando l’utente rilascia: invia se c’è testo ripulito, altrimenti chiude.
- La sola wake-word continua comunque a inviare automaticamente al silenzio; push-to-talk invia immediatamente al rilascio.
Implementato (9 dicembre 2025)
- Le sessioni dell’overlay ora trasportano un token per ogni acquisizione (wake-word o push-to-talk). Gli aggiornamenti partial/final/send/dismiss/level vengono scartati quando il token non corrisponde, evitando callback obsolete.
- Push-to-talk adotta qualsiasi testo dell’overlay visibile come prefisso (quindi premere il tasto rapido mentre l’overlay wake è attivo mantiene il testo e aggiunge il nuovo parlato). Attende fino a 1,5 s per una trascrizione finale prima di ripiegare sul testo corrente.
- Il logging di chime/overlay viene emesso a livello
infonelle categorievoicewake.overlay,voicewake.pttevoicewake.chime(avvio sessione, partial, final, send, dismiss, motivo del chime).
Prossimi passi
- VoiceSessionCoordinator (actor)
- Gestisce esattamente una
VoiceSessionalla volta. - API (basata su token):
beginWakeCapture,beginPushToTalk,updatePartial,endCapture,cancel,applyCooldown. - Scarta le callback che trasportano token obsoleti (impedisce ai vecchi recognizer di riaprire l’overlay).
- Gestisce esattamente una
- VoiceSession (modello)
- Campi:
token,source(wakeWord|pushToTalk), testo committed/volatile, flag chime, timer (auto-send, idle),overlayMode(display|editing|sending), scadenza del cooldown.
- Campi:
- Binding dell’overlay
VoiceSessionPublisher(ObservableObject) rispecchia la sessione attiva in SwiftUI.VoiceWakeOverlayViewesegue il rendering solo tramite il publisher; non modifica mai direttamente singleton globali.- Le azioni utente dell’overlay (
sendNow,dismiss,edit) richiamano il coordinator con il token della sessione.
- Percorso di invio unificato
- In
endCapture: se il testo ripulito è vuoto → chiudi; altrimentiperformSend(session:)(riproduce una sola volta il chime di invio, inoltra, chiude). - Push-to-talk: nessun ritardo; wake-word: ritardo facoltativo per l’invio automatico.
- Applica un breve cooldown al runtime wake dopo la fine di push-to-talk così la wake-word non si riattiva immediatamente.
- In
- Logging
- Il coordinator emette log
.infonel sottosistemaai.openclaw, categorievoicewake.overlayevoicewake.chime. - Eventi chiave:
session_started,adopted_by_push_to_talk,partial,finalized,send,dismiss,cancel,cooldown.
- Il coordinator emette log
Checklist di debug
-
Trasmetti i log durante la riproduzione di un overlay bloccato:
- Verifica che ci sia un solo token di sessione attivo; le callback obsolete dovrebbero essere scartate dal coordinator.
-
Assicurati che il rilascio di push-to-talk chiami sempre
endCapturecon il token attivo; se il testo è vuoto, aspettatidismisssenza chime né invio.
Passaggi di migrazione (suggeriti)
- Aggiungi
VoiceSessionCoordinator,VoiceSessioneVoiceSessionPublisher. - Esegui il refactor di
VoiceWakeRuntimein modo che crei/aggiorni/termini sessioni invece di toccare direttamenteVoiceWakeOverlayController. - Esegui il refactor di
VoicePushToTalkperché adotti le sessioni esistenti e chiamiendCaptureal rilascio; applica il cooldown al runtime. - Collega
VoiceWakeOverlayControlleral publisher; rimuovi le chiamate dirette da runtime/PTT. - Aggiungi test di integrazione per adozione della sessione, cooldown e chiusura con testo vuoto.