macOS companion app
Lớp phủ giọng nói
Vòng đời lớp phủ giọng nói (macOS)
Đối tượng: người đóng góp cho ứng dụng macOS. Mục tiêu: giữ cho lớp phủ giọng nói hoạt động có thể dự đoán khi từ đánh thức và nhấn để nói chồng lấp.
Ý định hiện tại
- Nếu lớp phủ đã hiển thị từ từ đánh thức và người dùng nhấn phím nóng, phiên phím nóng sẽ tiếp nhận văn bản hiện có thay vì đặt lại. Lớp phủ vẫn hiển thị trong khi phím nóng được giữ. Khi người dùng thả phím: gửi nếu có văn bản sau khi cắt khoảng trắng, nếu không thì đóng.
- Chỉ dùng từ đánh thức thì vẫn tự động gửi khi im lặng; nhấn để nói gửi ngay khi thả phím.
Đã triển khai (ngày 9 tháng 12 năm 2025)
- Các phiên lớp phủ giờ mang một token cho mỗi lần thu âm (từ đánh thức hoặc nhấn để nói). Các cập nhật tạm thời/cuối/gửi/đóng/mức sẽ bị bỏ khi token không khớp, tránh callback cũ.
- Nhấn để nói tiếp nhận mọi văn bản lớp phủ đang hiển thị làm tiền tố (vì vậy nhấn phím nóng khi lớp phủ đánh thức đang hiển thị sẽ giữ văn bản và nối thêm lời nói mới). Nó đợi tối đa 1,5 giây để nhận bản ghi cuối trước khi quay về văn bản hiện tại.
- Ghi log âm báo/lớp phủ được phát ra ở mức
infotrong các danh mụcvoicewake.overlay,voicewake.ptt, vàvoicewake.chime(bắt đầu phiên, tạm thời, cuối, gửi, đóng, lý do âm báo).
Bước tiếp theo
- VoiceSessionCoordinator (actor)
- Sở hữu đúng một
VoiceSessiontại một thời điểm. - API (dựa trên token):
beginWakeCapture,beginPushToTalk,updatePartial,endCapture,cancel,applyCooldown. - Bỏ các callback mang token cũ (ngăn bộ nhận dạng cũ mở lại lớp phủ).
- Sở hữu đúng một
- VoiceSession (mô hình)
- Trường:
token,source(wakeWord|pushToTalk), văn bản đã xác nhận/tạm thời, cờ âm báo, bộ hẹn giờ (tự động gửi, rảnh),overlayMode(display|editing|sending), thời hạn hồi chiêu.
- Trường:
- Liên kết lớp phủ
VoiceSessionPublisher(ObservableObject) phản chiếu phiên đang hoạt động vào SwiftUI.VoiceWakeOverlayViewchỉ kết xuất qua publisher; nó không bao giờ trực tiếp thay đổi singleton toàn cục.- Hành động người dùng trên lớp phủ (
sendNow,dismiss,edit) gọi ngược vào coordinator với token phiên.
- Đường gửi hợp nhất
- Khi
endCapture: nếu văn bản sau khi cắt khoảng trắng rỗng → đóng; nếu không thìperformSend(session:)(phát âm báo gửi một lần, chuyển tiếp, đóng). - Nhấn để nói: không trì hoãn; từ đánh thức: có thể trì hoãn để tự động gửi.
- Áp dụng một hồi chiêu ngắn cho runtime đánh thức sau khi nhấn để nói kết thúc để từ đánh thức không kích hoạt lại ngay lập tức.
- Khi
- Ghi log
- Coordinator phát log
.infotrong subsystemai.openclaw, danh mụcvoicewake.overlayvàvoicewake.chime. - Sự kiện chính:
session_started,adopted_by_push_to_talk,partial,finalized,send,dismiss,cancel,cooldown.
- Coordinator phát log
Danh sách kiểm tra gỡ lỗi
-
Phát trực tiếp log khi tái hiện lớp phủ bị kẹt:
sudo log stream --predicate 'subsystem == "ai.openclaw" AND category CONTAINS "voicewake"' --level info --style compact -
Xác minh chỉ có một token phiên đang hoạt động; callback cũ phải bị coordinator bỏ.
-
Đảm bảo khi thả nhấn để nói luôn gọi
endCapturevới token đang hoạt động; nếu văn bản rỗng, kỳ vọngdismissmà không có âm báo hoặc gửi.
Bước di chuyển (đề xuất)
- Thêm
VoiceSessionCoordinator,VoiceSession, vàVoiceSessionPublisher. - Tái cấu trúc
VoiceWakeRuntimeđể tạo/cập nhật/kết thúc phiên thay vì chạm trực tiếp vàoVoiceWakeOverlayController. - Tái cấu trúc
VoicePushToTalkđể tiếp nhận phiên hiện có và gọiendCapturekhi thả; áp dụng hồi chiêu runtime. - Nối
VoiceWakeOverlayControllervới publisher; loại bỏ các lệnh gọi trực tiếp từ runtime/PTT. - Thêm kiểm thử tích hợp cho tiếp nhận phiên, hồi chiêu và đóng khi văn bản rỗng.