macOS companion app
Surcouche vocale
Cycle de vie de la superposition vocale (macOS)
Public : contributeurs de l’app macOS. Objectif : garder la superposition vocale prévisible lorsque le mot d’activation et l’appuyer-pour-parler se chevauchent.
Intention actuelle
- Si la superposition est déjà visible à cause du mot d’activation et que l’utilisateur appuie sur le raccourci clavier, la session du raccourci clavier adopte le texte existant au lieu de le réinitialiser. La superposition reste affichée tant que le raccourci clavier est maintenu. Lorsque l’utilisateur relâche : envoyer s’il reste du texte après suppression des espaces, sinon fermer.
- Le mot d’activation seul continue à envoyer automatiquement en cas de silence ; l’appuyer-pour-parler envoie immédiatement au relâchement.
Implémenté (9 déc. 2025)
- Les sessions de superposition portent désormais un jeton par capture (mot d’activation ou appuyer-pour-parler). Les mises à jour partielles/finales/d’envoi/de fermeture/de niveau sont ignorées lorsque le jeton ne correspond pas, ce qui évite les rappels obsolètes.
- L’appuyer-pour-parler adopte tout texte de superposition visible comme préfixe (ainsi, appuyer sur le raccourci clavier pendant que la superposition de réveil est affichée conserve le texte et ajoute la nouvelle parole). Il attend jusqu’à 1,5 s une transcription finale avant de revenir au texte actuel.
- La journalisation du carillon/de la superposition est émise au niveau
infodans les catégoriesvoicewake.overlay,voicewake.pttetvoicewake.chime(début de session, partiel, final, envoi, fermeture, raison du carillon).
Étapes suivantes
- VoiceSessionCoordinator (acteur)
- Possède exactement une
VoiceSessionà la fois. - API (basée sur jeton) :
beginWakeCapture,beginPushToTalk,updatePartial,endCapture,cancel,applyCooldown. - Ignore les rappels qui portent des jetons obsolètes (empêche les anciens reconnaisseurs de rouvrir la superposition).
- Possède exactement une
- VoiceSession (modèle)
- Champs :
token,source(wakeWord|pushToTalk), texte validé/volatile, indicateurs de carillon, minuteurs (envoi automatique, inactivité),overlayMode(display|editing|sending), échéance du délai de récupération.
- Champs :
- Liaison de la superposition
VoiceSessionPublisher(ObservableObject) réplique la session active dans SwiftUI.VoiceWakeOverlayViews’affiche uniquement via le publisher ; il ne modifie jamais directement les singletons globaux.- Les actions utilisateur de la superposition (
sendNow,dismiss,edit) rappellent le coordinateur avec le jeton de session.
- Chemin d’envoi unifié
- Sur
endCapture: si le texte après suppression des espaces est vide → fermer ; sinonperformSend(session:)(joue le carillon d’envoi une fois, transfère, ferme). - Appuyer-pour-parler : aucun délai ; mot d’activation : délai facultatif pour l’envoi automatique.
- Appliquer un court délai de récupération au runtime de réveil après la fin de l’appuyer-pour-parler afin que le mot d’activation ne se redéclenche pas immédiatement.
- Sur
- Journalisation
- Le coordinateur émet des journaux
.infodans le sous-systèmeai.openclaw, catégoriesvoicewake.overlayetvoicewake.chime. - Événements clés :
session_started,adopted_by_push_to_talk,partial,finalized,send,dismiss,cancel,cooldown.
- Le coordinateur émet des journaux
Liste de vérification de débogage
-
Diffuser les journaux pendant la reproduction d’une superposition bloquée :
sudo log stream --predicate 'subsystem == "ai.openclaw" AND category CONTAINS "voicewake"' --level info --style compact -
Vérifier qu’un seul jeton de session active existe ; les rappels obsolètes doivent être ignorés par le coordinateur.
-
S’assurer que le relâchement de l’appuyer-pour-parler appelle toujours
endCaptureavec le jeton actif ; si le texte est vide, attendredismisssans carillon ni envoi.
Étapes de migration (suggérées)
- Ajouter
VoiceSessionCoordinator,VoiceSessionetVoiceSessionPublisher. - Refactoriser
VoiceWakeRuntimepour créer/mettre à jour/terminer les sessions au lieu de toucher directementVoiceWakeOverlayController. - Refactoriser
VoicePushToTalkpour adopter les sessions existantes et appelerendCaptureau relâchement ; appliquer le délai de récupération du runtime. - Connecter
VoiceWakeOverlayControllerau publisher ; supprimer les appels directs depuis le runtime/PTT. - Ajouter des tests d’intégration pour l’adoption de session, le délai de récupération et la fermeture avec texte vide.