Skip to content

fix(assistant): hands-free second turn — release audio before re-arming, settle delay, dead-mic watchdog - #690

Merged
thedancingdeveloper merged 1 commit into
mainfrom
fix/handsfree-second-turn
Sep 10, 2026
Merged

thedancingdeveloper merged 1 commit into
mainfrom
fix/handsfree-second-turn

Conversation

@thedancingdeveloper

Copy link
Copy Markdown
Contributor

On the phone the first hands-free turn now works end to end (capture → reply → spoken via Web Audio); the second turn never captured — "Listening…" for 30 s (max_turn) with no partial, no speech-start, no stop. The diagnostics show turn 2's mic opening 31 ms after the app's own playback ended, with the AudioContext still holding the output stream. And the speech plugin cannot report a native start failure: with partialResults it resolves start() at once, and its onError only rejects that already-resolved call and stops listening without notifying JS — a recogniser that comes up dead is invisible.

Fix (client-only):

  • suspendAudio() releases the output stream when a reply ends or is halted, before any mic re-opens behind it.
  • voiceTurn: reopen_delay_ms (400) settle before re-arming; mic_watchdog_ms (8000) restarts a mic that reports nothing, bounded by max_mic_restarts (2); recognizerStarted() (plugin listeningState: started) disarms it. Mute during the wait cancels the re-open; end during it tears down immediately.
  • Conversation capture emits rec.start / rec.state / rec.partial diagnostics; watchdog restarts are logged.

Tests: settle wait, mute/end during the wait, watchdog restart + cap, disarm on liveness, suspend-once; existing cases pinned to immediate re-open. Real-browser voice-turn, hands-free, and barge-in Playwright tests pass. Typecheck clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_01YG9Nsxqc6CREb56Uisnmmy

…ng, settle, watchdog

On the phone the first hands-free turn worked end to end (capture → reply →
spoken via Web Audio); the second never captured: the chip said Listening…
for 30 s (max_turn) twice, with no partial, no speech-start, no stop. The
diagnostics show turn 2's mic opening 31 ms after the app's own playback
ended, with the AudioContext still holding the output stream — and the
speech plugin cannot report a native start failure at all: with
partialResults it resolves start() at once, and its onError only rejects
that already-resolved call and stops listening without notifying JS. A
recogniser that comes up dead is invisible.

- audioPlayback.suspendAudio(): release the output stream when a reply ends
  naturally or is halted, before any mic re-opens behind it.
- voiceTurn: `reopen_delay_ms` (400) settle time before re-arming after a
  reply (and after an empty turn / a watchdog restart); `mic_watchdog_ms`
  (8000): a freshly opened mic that reports nothing — no partial, no speech
  start, no stop — is closed and re-opened, bounded by `max_mic_restarts`
  (2), then max_turn / idle apply as before. New `recognizerStarted()` event
  (the plugin's listeningState: started) disarms the watchdog. A mute during
  the settle wait cancels the re-open; ending during it tears down at once
  (no mic in flight to protect). Optional `onRecognizerRestart` port.
- Assistant.tsx: conversation capture emits rec.start / rec.state /
  rec.partial diagnostics and feeds started → recognizerStarted; watchdog
  restarts are logged. The finished TTS request is released on natural end.
- Tests: settle wait, mute/end during the wait, watchdog restart + cap,
  disarm on liveness, suspend-once; existing cases pinned to an immediate
  re-open. Real-browser voice-turn, hands-free and barge-in tests pass.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YG9Nsxqc6CREb56Uisnmmy
@thedancingdeveloper
thedancingdeveloper merged commit 6e21240 into main Sep 10, 2026
15 checks passed
@thedancingdeveloper
thedancingdeveloper deleted the fix/handsfree-second-turn branch September 10, 2026 11:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant