macOS companion app

Голосовий оверлей

Життєвий цикл голосового накладання (macOS)

Аудиторія: контриб’ютори застосунку macOS. Мета: забезпечити передбачувану поведінку голосового накладання, коли фраза активації та режим «натисни й говори» перетинаються.

Поточний задум

  • Якщо накладання вже видиме через фразу активації, а користувач натискає гарячу клавішу, сесія гарячої клавіші приймає наявний текст замість його скидання. Накладання залишається відкритим, доки гаряча клавіша утримується. Коли користувач відпускає її: надіслати, якщо є обрізаний текст, інакше закрити.
  • Одна лише фраза активації досі автоматично надсилає після тиші; режим «натисни й говори» надсилає одразу після відпускання.

Реалізовано (9 грудня 2025 р.)

  • Сесії накладання тепер мають токен для кожного захоплення (фраза активації або режим «натисни й говори»). Оновлення часткового/фінального тексту, надсилання, закриття та рівня відкидаються, коли токен не збігається, що запобігає застарілим callback.
  • Режим «натисни й говори» приймає будь-який видимий текст накладання як префікс (тож натискання гарячої клавіші, поки накладання активації відкрите, зберігає текст і додає нове мовлення). Він очікує до 1,5 с на фінальну транскрипцію, перш ніж повернутися до поточного тексту.
  • Журналювання сигналу/накладання виводиться на рівні info в категоріях voicewake.overlay, voicewake.ptt і voicewake.chime (початок сесії, частковий текст, фінальний текст, надсилання, закриття, причина сигналу).

Наступні кроки

  1. VoiceSessionCoordinator (actor)
    • У кожен момент володіє рівно однією VoiceSession.
    • API (на основі токенів): beginWakeCapture, beginPushToTalk, updatePartial, endCapture, cancel, applyCooldown.
    • Відкидає callback із застарілими токенами (запобігає повторному відкриттю накладання старими розпізнавачами).
  2. VoiceSession (model)
    • Поля: token, source (wakeWord|pushToTalk), зафіксований/тимчасовий текст, прапорці сигналу, таймери (автоматичне надсилання, бездіяльність), overlayMode (display|editing|sending), крайній строк cooldown.
  3. Прив’язка накладання
    • VoiceSessionPublisher (ObservableObject) віддзеркалює активну сесію у SwiftUI.
    • VoiceWakeOverlayView рендериться лише через publisher; він ніколи не змінює глобальні singletons напряму.
    • Дії користувача в накладанні (sendNow, dismiss, edit) викликають координатор із токеном сесії.
  4. Уніфікований шлях надсилання
    • На endCapture: якщо обрізаний текст порожній → закрити; інакше performSend(session:) (відтворює сигнал надсилання один раз, пересилає, закриває).
    • Режим «натисни й говори»: без затримки; фраза активації: необов’язкова затримка для автоматичного надсилання.
    • Застосувати короткий cooldown до wake runtime після завершення режиму «натисни й говори», щоб фраза активації не запускалася повторно негайно.
  5. Журналювання
    • Координатор виводить журнали .info у subsystem 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
    
  • Перевірте, що активний лише один токен сесії; застарілі callback має відкидати координатор.

  • Переконайтеся, що відпускання в режимі «натисни й говори» завжди викликає endCapture з активним токеном; якщо текст порожній, очікуйте dismiss без сигналу чи надсилання.

Кроки міграції (запропоновано)

  1. Додати VoiceSessionCoordinator, VoiceSession і VoiceSessionPublisher.
  2. Переробити VoiceWakeRuntime, щоб створювати/оновлювати/завершувати сесії замість прямого звернення до VoiceWakeOverlayController.
  3. Переробити VoicePushToTalk, щоб приймати наявні сесії та викликати endCapture після відпускання; застосувати cooldown runtime.
  4. Під’єднати VoiceWakeOverlayController до publisher; прибрати прямі виклики з runtime/PTT.
  5. Додати інтеграційні тести для прийняття сесії, cooldown і закриття за порожнього тексту.

Пов’язане