diff --git a/hooks/useVoiceInteraction.ts b/hooks/useVoiceInteraction.ts index 0280df0..67f0e36 100644 --- a/hooks/useVoiceInteraction.ts +++ b/hooks/useVoiceInteraction.ts @@ -315,6 +315,14 @@ export function useVoiceInteraction({ } if (event.type === 'input_audio_buffer.speech_started') { + // A fresh user turn is beginning. Reset the finalization gate so + // that if a previous turn's `onFinalTranscript` hasn't resolved yet + // (it's an awaited fetch), the upcoming + // `input_audio_transcription.completed` event isn't silently + // dropped by the `if (isFinalizingTurnRef.current) return;` guard. + // The previous turn's in-flight fetch will still complete and + // update state; it just won't block the next turn from finalizing. + isFinalizingTurnRef.current = false; finalBufferRef.current = ''; assistantBufferRef.current = ''; lastFinalizedTranscriptRef.current = ''; @@ -581,8 +589,18 @@ export function useVoiceInteraction({ const remoteStream = event.streams[0]; if (!remoteStream) return; if (!remoteAudioRef.current) { - remoteAudioRef.current = new Audio(); - remoteAudioRef.current.autoplay = true; + const audio = new Audio(); + audio.autoplay = true; + // Keep the element in the DOM. Some browsers (notably Safari on iOS) + // treat detached