docs/design/2026-09-01-qwen-live-tool-continuation-permission-resume.md
DashScope Realtime does not automatically continue after a
function_call_output. The standalone Live client currently submits the tool
output without requesting another response, so result-bearing tools leave the
conversation silent until the user speaks again.
Creating every continuation immediately is also incorrect. User speech owns
the current turn: backend results that arrive while the user is speaking must
be retained and folded into the answer to that utterance, rather than racing
the provider's direct response or speaking over the user. A second lifecycle
gap exists after response.done, because the Host can still have buffered
audio even though the provider response is no longer active.
Permission requests have a second lifetime mismatch. ACP sessions outlive a Live call, and an injected spoken ask can also be interrupted or ignored while the backend request remains parked. Delivery of speech is therefore not proof that the permission was resolved.
conversation.item.created for the VAD input item or
input_audio_buffer.committed; treat those as idempotent forms of the same
event. After the input commit callback synchronously drains queued backend
context, create exactly one direct response. Tool results and backend speech
requests that arrive during the open user turn coalesce into that response
rather than creating another one. If a merge arrives after response.create
has left the socket but before its response.created acknowledgement,
cancel that unacknowledged request and replace it after the merged context so
the user still hears one combined answer. Keep a cancelled response active
until DashScope confirms response.done; only then release the response slot
and create its replacement. If another utterance starts first, retire the
older queued input and ignore its late ASR completion.handoff receipts do not get a redundant acknowledgement
response; their later backend events remain the user-visible result.speech_started through input commit,
not merely through speech_stopped. Starting new speech also ends any old
playback quiet-gap estimate, even when the audio itself has already ended.response.done.waiting_for_permission from session status tools, including the
request handle and human-readable title needed to answer it.remain_silent.qwen-acp and qodercli.