chat/voice: land voice answers on question carousels (fix Skipped)#323161
Open
accnops wants to merge 3 commits into
Open
chat/voice: land voice answers on question carousels (fix Skipped)#323161accnops wants to merge 3 commits into
accnops wants to merge 3 commits into
Conversation
added 3 commits
June 26, 2026 12:43
…ssion
The voice client collapsed five distinct pending chat constructs into a single
agent_state:"waiting_for_confirmation" and dropped the option list, question
ids, and routing ids, so a voice answer to a questionCarousel form could never
land. The dispatch layer also only knew how to confirmWith a toolInvocation.
Break A (wire): voiceSessionController now emits a typed `pending` payload per
session carrying type (questions|approval|elicitation), the exact
pending_id/request_id/resolve_id/approval_kind, and the per-question schema with
options serialized in displayed (defaults-first) order so a spoken ordinal
matches the screen. The generic delta computer ships it and nulls it on clear.
Break C (apply): voiceToolDispatchService handles respond_to_session by exact
session+part resolution and applies per kind — confirmWith for
toolInvocation/postApproval, accept/reject for elicitation2, confirmation
resend, and notifyQuestionCarouselAnswer for carousels. Answers are resolved
deterministically against the live options (value -> 1-based ordinal -> label,
freeform fallback) and a structured {ok,reason} result lets the backend narrate
corrections.
Extract the carousel option-ordering into chatQuestionCarouselHelpers.ts and
reuse it from the widget, controller, and dispatch so all three agree on order;
add a unit test for ordering and answer resolution.
Lockstep with the voice_code backend respond_to_session protocol change.
A voice answer to a questionCarousel showed "Skipped" and never reached the agent for vscode-chat-session://local/... (agent-host) sessions. The dispatch layer resolved a carousel only by firing notifyQuestionCarouselAnswer, an event consumed solely by the askQuestions and openBrowser tools. Agent-host carousels resolve exclusively via their completion promise (carousel.completion.complete), which dispatches ChatInputCompleted back to the agent; voice never settled it, so the carousel was abandoned and later disposed empty -> isUsed -> "Skipped". Fix: the answer path now calls carousel.dismiss(answers) when the live part is a ChatQuestionCarouselData, in addition to the event. dismiss settles the completion promise (the only agent-host resolution path) and marks the carousel used in one idempotent call, mirroring the real UI submit which does both. Empty resolved answers (the user answered nothing) map to undefined via a new toCarouselAnswers helper so agent-host treats it as a cancel/skip rather than submitting an empty accept. Unit-tested.
…nswers ChatQuestionCarouselPart.skip()/ignore() only guarded on the part's own _isSkipped and allowSkip. If the underlying carousel was resolved elsewhere (e.g. a voice answer calling dismiss()) after the part had already rendered interactively, a subsequent onDidSubmitRequest auto-skip would still invoke onSubmit and overwrite carousel.data with default answers, so the summary showed defaults instead of the answer that actually landed. Guard both methods on carousel.isUsed so an already-resolved carousel is never re-submitted from the UI part, regardless of how it was resolved. Adds regression tests for the external-resolution race.
Contributor
|
@accnops please read the following Contributor License Agreement(CLA). If you agree with the CLA, please reply with the following information.
Contributor License AgreementContribution License AgreementThis Contribution License Agreement (“Agreement”) is agreed to by the party signing below (“You”),
|
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
Lets voice users answer VS Code chat question carousel multi-question forms (and approvals) by voice. Fixes the end-to-end break where a voice-spoken selection never landed and the card showed "Skipped".
Three commits, stacked on
meganrogge/voice-mode-fixes:respond_to_sessionanswer/approval plumbing — voice tool dispatch routes question-carousel answers and approvals back into the chat session.carousel.dismiss(answers)→ChatInputCompleted), not the event the voice path fired. The answer branch now callscarousel.dismiss(toCarouselAnswers(answers))in addition tonotifyQuestionCarouselAnswer. An empty answer record maps toundefinedso a genuine skip becomes Cancel rather than a truthy empty Accept. Adds a puretoCarouselAnswershelper + unit tests.dismiss(), a lateronDidSubmitRequestauto-skip could callhandleSubmit(defaults)and overwrite the resolved answers.skip()/ignore()now also guard oncarousel.isUsed. Adds 2 regression tests.Testing
tsc -p src/tsconfig.json --noEmit— 0 errorstoCarouselAnswersand the carousel skip/ignore guards.