Skip to content

agentHost: cancel late callbacks during abort#327427

Draft
connor4312 wants to merge 2 commits into
mainfrom
connor4312/agent-host-abort-callbacks
Draft

agentHost: cancel late callbacks during abort#327427
connor4312 wants to merge 2 commits into
mainfrom
connor4312/agent-host-abort-callbacks

Conversation

@connor4312

Copy link
Copy Markdown
Member

Summary

  • track the Copilot SDK abort lifecycle explicitly
  • cancel existing permission, user-input, elicitation, plan-review, MCP, sampling, auto-approval, and client-tool waits when abort begins
  • reject late interactive callbacks until the aborted session becomes idle, preventing accepted steering from wedging replacement turns
  • add deterministic session-level coverage and a record-only Copilot E2E scenario for steering followed by abort

Context

Agent Host debug logs showed an accepted steering message being echoed and executed by the SDK after its protocol turn was cancelled. Agent Host had already cleared the steering correlation and advanced to a replacement turn, causing SDK tool actions to retain stale turn identity. A later permission request was not visible in synchronized UI state and blocked all follow-up work.

Validation

  • client TypeScript type-check
  • focused copilotAgentSession.test.ts abort callback tests
  • record-only E2E test compiles and is registered; deterministic replay skips it because replay has no real steering/abort timing window
  • targeted hygiene checks

The pre-commit hook could not find npm in this environment, so it skipped the package precommit command; the equivalent targeted hygiene checks were run manually.

Keep Copilot SDK callbacks from parking after an accepted steering message races with abort, allowing replacement turns and follow-ups to proceed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 25, 2026 00:07
@connor4312
connor4312 enabled auto-merge (squash) July 25, 2026 00:07
Read abort state through a method so TypeScript does not retain stale enum narrowing across awaits.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

This PR hardens the Copilot agent host against late/straggling interactive callbacks that arrive after an SDK abort begins, preventing stale-turn interactions (e.g., accepted steering) from wedging subsequent replacement turns.

Changes:

  • Track an explicit “abort in progress” lifecycle and proactively cancel/deny pending interactive waits during abort.
  • Reject interactive/tool callbacks that arrive while abort is in progress, until the session becomes idle again.
  • Add focused unit coverage for abort-time cancellation plus a record-only E2E scenario for “steering then abort then replacement turn”.
Show a summary per file
File Description
src/vs/platform/agentHost/test/node/e2e/providers/copilotAgentHostE2E.integrationTest.ts Adds a record-only E2E test validating steering + abort doesn’t block a replacement turn.
src/vs/platform/agentHost/test/node/e2e/KNOWN_ISSUES.md Documents why abort-related tests are record-only under deterministic replay.
src/vs/platform/agentHost/test/node/copilotAgentSession.test.ts Adds unit coverage verifying interactive callbacks are cancelled/denied while abort is in progress.
src/vs/platform/agentHost/node/copilot/copilotAgentSession.ts Implements abort lifecycle tracking and cancels/denies interactive waits + rejects late callbacks during abort.

Review details

  • Files reviewed: 4/4 changed files
  • Comments generated: 3
  • Review effort level: Low

Comment on lines 1543 to 1545
// call `resetTurnState` just before `send()`; this covers the
// direct-send path and is a no-op when the turn already exists.
this.resetTurnState(turnId, senderClientId);
Comment on lines +1103 to +1105
if (this._abortState === CopilotAbortState.Aborting) {
return this._toolSearchFailure('Tool call cancelled: session is aborting');
}
Comment on lines 2183 to 2186
const toolCallId = request.toolCallId;
if (!toolCallId) {
// TODO: handle permission requests without a toolCallId by creating a synthetic tool call
this._logService.warn(`[Copilot:${this.sessionId}] Permission request without toolCallId, auto-denying: kind=${request.kind}`);
@connor4312
connor4312 disabled auto-merge July 25, 2026 00:28
@connor4312
connor4312 marked this pull request as draft July 25, 2026 00:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants