chat: true mid-turn steering for cloud sessions#325368
Conversation
There was a problem hiding this comment.
Pull request overview
Fixes queued steering messages becoming stranded after streamed contributed-session turns complete.
Changes:
- Flushes pending requests after streamed-response completion.
- Adds regression coverage for queued steering messages.
Show a summary per file
| File | Description |
|---|---|
chatServiceImpl.ts |
Processes queued requests after stream completion. |
chatService.test.ts |
Tests steering-message delivery after completion. |
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 3
- Review effort level: Medium
5c7b4c6 to
23d50e5
Compare
…pletes Contributed chat sessions whose in-progress turn is streamed via activeResponseCallback (progressObs) — e.g. Copilot cloud sessions — register a synthetic pending request while streaming. A message sent mid-turn is therefore queued as a steering/queued message. The streaming-completion branch in loadRemoteSession deleted that pending request without flushing the queue, so the queued message was stranded in the model forever and never sent (steering 'limbo'). Mirror the normal request-completion path by calling processPendingRequests when the streamed turn completes. It is a no-op while another request is in flight and returns early for server-managed (agent-host) queues, so it is safe on every completion tick. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
23d50e5 to
a556676
Compare
Builds on the queued-message flush fix to inject a steer into a live cloud turn instead of only sending it after the turn settles, matching github.com/copilot/agents. Core (chatServiceImpl): for non-server-managed streamed (activeResponseCallback) sessions, dispatch a queued Steering message immediately when the in-flight pending request is the synthetic streamed-turn tracker (no requestId) — never over a real in-flight request. Re-establish tracking on the next progress tick so the turn stays in-progress. Extension (copilotCloudSessionsProvider): - Track live TaskTurnStreamer taskIds; when a stream is already active, handleTaskFollowUp only POSTs /steer and lets the running stream render the injected result (no second streamer). - Make the follow-up stream yield-aware (mirrors the Copilot CLI provider): poll context.yieldRequested and return on yield so the chat service flushes the next steer immediately. - Choose the render mode from the pre-steer task state: a steer into an active turn renders mode:'current' (the injection has no new task.sessions[] row, so mode:'next' would time out); a steer onto a settled task renders mode:'next'. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Added a second commit that builds on the flush fix to implement true mid-turn steering for cloud sessions (previously the steer only sent after the turn settled — queue behavior — rather than injecting into the running turn like github.com/copilot/agents does). Core ( Extension (
Verified end-to-end against a live cloud task: the steer POSTs within ~100ms of submitting, and the injected message + continued output render inline. |
…stale task state - Identify the synthetic streamed-turn pending request via an explicit WeakSet instead of `requestId === undefined`: real requests are inserted into _pendingRequests before their requestId is assigned, so the old guard could delete a live request and start a concurrent invocation. - Restore in-progress tracking deterministically when the immediate-steer dispatch settles (hook responseCompletePromise) instead of relying on a future progress tick that may never fire. - Cloud provider: treat a follow-up as mid-turn (render mode:'current') only when BOTH the task and its latest turn are active, so a terminal task with a stale in_progress latest turn doesn't pick a streamer that exits before the steered turn appears. - Rework the regression tests: the steering test now asserts immediate dispatch, no duplicate dispatch on completion, and preserved in-progress tracking; a separate test covers completion-flush of a non-steering queued message. - Condense multi-line inline comments. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Problem
When steering a Copilot cloud chat session mid-turn, the message was stranded: it showed a "STEERING" pending bubble that only sent after the turn finished (queue behavior), rather than injecting into the running turn the way github.com/copilot/agents does.
Scope
This PR now delivers true mid-turn steering for cloud sessions, in three layers:
activeResponseCallback) turn is flushed when the turn completes (was previously stranded forever)./tasks/{id}/steerand the running stream renders the injected result, matching github.com/copilot/agents.Core (
chatServiceImpl)For non-server-managed streamed sessions:
Steeringmessage immediately, but only when the in-flight pending request is the synthetic streamed-turn tracker (identified via an explicitWeakSet, since real requests are inserted into_pendingRequestsbefore theirrequestIdis assigned) — never a real in-flight request.responseCompletePromise), sorequestInProgressstays true for the still-active stream.Queuedmessages are unaffected and still flush on completion.Extension (
copilotCloudSessionsProvider)TaskTurnStreamertask ids; when a stream is already active,handleTaskFollowUponly POSTs the steer (no second streamer).context.yieldRequestedand cancels only its local streamer on yield, so the chat service flushes the next steer immediately.mode:'current'(the injection produces no newtask.sessions[]row, somode:'next'would time out); a steer onto a settled task rendersmode:'next'.Testing
Unit tests in
chatService.test.ts:Queuedmessage waits during the turn and is flushed when it completes.Verified end-to-end against a live cloud task: the steer POSTs within ~100ms and the injected message + continued output render inline.
typecheck-clientand the copilot extension typecheck are clean.Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com