macOS companion app
Голосовой оверлей
Аудитория: разработчики macOS приложения. Цель: обеспечить предсказуемость голосового оверлея при пересечении wake-word и push-to-talk.
Текущий замысел
- Если оверлей уже виден из-за wake-word и пользователь нажимает горячую клавишу, сессия горячей клавиши принимает существующий текст вместо его сброса. Оверлей остается на экране, пока клавиша удерживается. Когда пользователь отпускает: отправить, если есть обрезанный текст, в противном случае скрыть.
- Wake-word по-прежнему отправляет автоматически при тишине; push-to-talk отправляет немедленно при отпускании.
Реализовано (9 декабря 2025)
- Сессии оверлея теперь используют токен для каждого захвата (wake-word или push-to-talk). Частичные/финальные/отправленные/скрытые обновления и обновления уровня отбрасываются, если токен не совпадает, что позволяет избежать устаревших обратных вызовов.
- Push-to-talk принимает любой видимый текст оверлея в качестве префикса (так что нажатие горячей клавиши, когда оверлей wake-word активен, сохраняет текст и добавляет новую речь). Он ждет до 1,5 секунд финальной транскрипции, прежде чем вернуться к текущему тексту.
- Логирование звуковых сигналов/оверлея выводится на уровне
infoв категорияхvoicewake.overlay,voicewake.pttиvoicewake.chime(начало сессии, частичный, финальный, отправка, скрытие, причина звукового сигнала).
Следующие шаги
- VoiceSessionCoordinator (актор)
- Владеет ровно одной
VoiceSessionв каждый момент времени. - API (на основе токена):
beginWakeCapture,beginPushToTalk,updatePartial,endCapture,cancel,applyCooldown. - Отбрасывает обратные вызовы с устаревшими токенами (предотвращает повторное открытие оверлея старыми распознавателями).
- Владеет ровно одной
- VoiceSession (модель)
- Поля:
token,source(wakeWord|pushToTalk), зафиксированный/изменчивый текст, флаги звукового сигнала, таймеры (автоотправка, бездействие),overlayMode(отображение|редактирование|отправка), срок действия перерыва.
- Поля:
- Привязка оверлея
VoiceSessionPublisher(ObservableObject) отражает активную сессию в SwiftUI.VoiceWakeOverlayViewотображает данные только через издатель; он никогда не изменяет глобальные синглтоны напрямую.- Действия пользователя в оверлее (
sendNow,dismiss,edit) вызывают обратный вызов в координатор с токеном сессии.
- Унифицированный путь отправки
- При
endCapture: если обрезанный текст пуст → скрыть; иначеperformSend(session:)(один раз воспроизводит звуковой сигнал отправки, пересылает, скрывает). - Push-to-talk: без задержки; wake-word: опциональная задержка для автоотправки.
- Применить короткий перерыв для wake-word рантайма после завершения push-to-talk, чтобы wake-word не срабатывал немедленно повторно.
- При
- Логирование
- Координатор выводит логи
.infoв подсистемеai.openclaw, категорииvoicewake.overlayиvoicewake.chime. - Ключевые события:
session_started,adopted_by_push_to_talk,partial,finalized,send,dismiss,cancel,cooldown.
- Координатор выводит логи
Чеклист для отладки
-
Потоковое логирование при воспроизведении зависшего оверлея:
Копировать
sudo 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к издателю; удалите прямые вызовы из runtime/PTT. - Добавьте интеграционные тесты для принятия сессии, перерыва и скрытия при пустом тексте.