feat(conversation): call/SMS confirmation and multi-turn follow-up#25
Merged
Conversation
- ConfirmationParser: maps verbal yes/no to AFFIRM/DENY; questionFor() generates "Call Name?" / "Text Name?" prompts - ConversationContext: rolling 3-turn history (ArrayDeque, drops oldest) - InferenceManager: multi-turn prompt builder; history injected into Gemma chat template with system prompt anchored to first user turn - GlassesAIService: AWAITING_CONFIRMATION and FOLLOW_UP_LISTENING states; ReListenMode enum drives afterOnDeviceTurnComplete() branching; SCO and audioTrack kept alive for re-listen window; startReListenRecording() recreates glassesMicRecord, posts timeout runnable (8 s confirmation, 5 s follow-up); conversationContext populated on each FOLLOW_UP turn; cleared on new wake word trigger or cancellation Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…n beep Whisper returns "Yes." / "No." with trailing punctuation — the parser's startsWith exact match failed, causing all confirmations to cancel. Strip .,!? before matching so "Yes." → "yes" → AFFIRM. Also play ascending beep when entering AWAITING_CONFIRMATION or FOLLOW_UP_LISTENING so the user knows Prism is ready to listen. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
No beep in either direction when entering confirmation or follow-up listening windows. Descending end-of-turn chime only plays when reListenMode is NONE (Prism is done listening entirely). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…lose Move descending chime out of TTS thread and into afterOnDeviceTurnComplete() (mode == NONE path only). Re-listen transitions (confirmation, follow-up) are now completely silent — no chime when the mic pauses between turns. Ascending chime remains only in onScoConnected() at session start. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…acts Stopping and recreating glassesMicRecord between turns caused audible hardware artifacts (perceived as chimes) on the glasses SCO audio path. Now the AudioRecord stays alive for the entire session — re-listen just starts a new reader thread without touching the underlying mic handle. Stop/release only happens on full session teardown (mode == NONE). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…DENY glassesMicRecord stays alive across turns, so its internal buffer holds audio recorded during TTS playback. Reading that echo immediately caused speechStarted=true, a junk transcript, a DENY, and a full session teardown (descending beep) followed by SCO reconnect (ascending beep). Drain 4 chunks (~400ms) before entering the VAD loop so only fresh microphone audio from the user is evaluated. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
audioTrackstay alive across turnsafterOnDeviceTurnComplete()branches onReListenMode(CONFIRMATION / FOLLOW_UP / NONE) — skips SCO/AudioTrack teardown and callsstartReListenRecording()instead; context clears on new wake word trigger, denial, or timeoutNew files
ConfirmationParser.ktAFFIRM/DENY;questionFor()generates confirmation promptConversationContext.ktArrayDeque, drops oldest on overflow)ConfirmationParserTest.ktConversationContextTest.ktInferenceManagerPromptTest.ktTest plan
./gradlew :app:test🤖 Generated with Claude Code