feat: voice command mapping — context-gated approve/reject over voice
While a tool-permission gate is held on the active tab, spoken confirm phrases resolve it via the existing approve/reject WS channel — no server change (byte-shuttle intact). Everything else stays ordinary dictation. Safety: whole-utterance-exact matching + leading-negation guard + reject precedence + confidence gate (approve only) + a cancellable 1.5s confirm window whose commit re-validates gate identity/epoch/connectivity (TOCTOU guard) + stale-gate epoch bound at PTT-start. New pure modules public/voice-commands.ts and public/voice-confirm.ts at 100% coverage; wiring in voice/terminal-session/tabs/main. 1307 tests pass, typecheck clean, build:web OK. Independent code + security reviews flagged a confirm-window TOCTOU and an unwired cancel path — both fixed and covered. Plan: docs/PLAN_VOICE_COMMANDS.md.
This commit is contained in:
@@ -42,8 +42,13 @@ if (joinId) {
|
||||
let settings: Settings = loadSettings()
|
||||
app.applySettings(settings)
|
||||
|
||||
// Key bar (mobile + desktop) sends to whichever tab is active.
|
||||
mountKeybar((data) => app.sendToActive(data))
|
||||
// Key bar (mobile + desktop) sends to whichever tab is active. onVoiceTrigger
|
||||
// activates the full voice path (dictation + context-gated confirm/reject, VC)
|
||||
// and un-dormants the 🎤 button (createVoiceInput / matchCommand are otherwise
|
||||
// unreachable dead code without this wiring).
|
||||
mountKeybar((data) => app.sendToActive(data), {
|
||||
onVoiceTrigger: (action) => app.handleVoiceTrigger(action),
|
||||
})
|
||||
|
||||
// Multi-device: when this device regains focus, re-assert the active tab's size
|
||||
// so a shared session snaps to THIS screen (latest-writer-wins) — full-screen on
|
||||
|
||||
Reference in New Issue
Block a user