Skip to content

agentHost: parallelize message send and git checkpoint capture (experiment) - #339195

Draft
Vijay Upadya (vijayupadya) wants to merge 2 commits into
mainfrom
vijayu/ah-perf3-async-checkpoint
Draft

Vijay Upadya (vijayupadya) wants to merge 2 commits into
mainfrom
vijayu/ah-perf3-async-checkpoint

Conversation

@vijayupadya

@vijayupadya Vijay Upadya (vijayupadya) commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Summary

What's slow today: before the agent host sends your message to Copilot, it saves a snapshot of your workspace (a git checkpoint) so the agent's edits can be undone later. The message waits until that snapshot is finished. On Windows that takes 200–500 ms in a large repo, and it happens on every message, so the chat looks idle for that long before anything appears.

What this PR changes: the message is sent right away, and the snapshot is saved at the same time. The model can start thinking and responding immediately. The snapshot only has to be finished before anything changes the workspace, so every tool call (file edits, terminal commands, subagents) and every plugin hook command waits for it.

Result: on Windows, follow-up messages reach the model about 150–200 ms (around 30%) sooner, with the same undo and checkpoint guarantees as before.

It's off by default and behind the experiment setting chat.agentHost.experimental.deferTurnStartCheckpoint. Follow-up to #339053.

Before: the message waits for the snapshot

sequenceDiagram
    autonumber
    participant AH as Agent host
    participant Git as Checkpoint (git)
    participant CLI as Copilot runtime
    participant M as Model

    AH->>Git: capture turn-start checkpoint
    Git-->>AH: done (200–500 ms on Windows)
    AH->>CLI: send(turn)
    CLI->>M: first model request
    M-->>CLI: tool call
    CLI->>CLI: run tool
Loading

After: the message is sent while the snapshot is saved, and tools and hook commands wait for it

sequenceDiagram
    autonumber
    participant AH as Agent host
    participant Git as Checkpoint (git)
    participant CLI as Copilot runtime
    participant M as Model

    par
        AH->>Git: capture turn-start checkpoint
    and
        AH->>CLI: send(turn, turnStartBarrier)
        CLI->>M: first model request
    end
    Git-->>AH: done
    M-->>CLI: tool call
    CLI->>AH: tool and hook commands wait for turnStartBarrier
    note over AH,CLI: Also applies to plugin hooks (SessionStart, UserPromptSubmit, ...)
    AH-->>CLI: barrier settled
    CLI->>CLI: run tool
Loading

Changes

  • agentSideEffects.ts: when the provider supports it (IAgentChats.supportsTurnStartBarrier):
    • creates a turnStartBarrier that settles with the checkpoint capture and hands it to both prepareTurn and sendMessage;
    • with the setting on, dispatch no longer waits for the capture;
    • the experiment trigger fires in both arms. It's generalized to _reportExperimentTrigger(settingId) and held until the assignment context is available.
  • copilotAgent.ts: keeps the current barrier for each chat. A session that is still starting (before send()) reads it from there, so SessionStart hooks also wait.
  • copilotAgentSession.ts: _handlePreToolUse waits for the barrier before any tool runs, and the runtime exposes waitForTurnStartBarrier() for hooks.
  • copilotPluginConverters.ts: every plugin hook command (PreToolUse, PostToolUse, UserPromptSubmit, SessionStart, SessionEnd, ErrorOccurred) waits for the barrier before it runs. Hooks with no plugin commands don't wait.
  • agentHostCheckpointService.ts:
    • getBaselineCheckpoint and getTurnCheckpointPair wait for any in-flight turn-start capture, so readers never see a missing baseline;
    • internal callers use a variant that doesn't wait, to avoid a deadlock.
  • Behavior change when deferred: a failed capture is logged and the turn continues without a checkpoint, instead of failing the turn.
  • Telemetry: sendStageCheckpointMs is left out whenever deferral is on and the provider supports the barrier, even if the capture already finished (documented in OTEL.md). timeToProviderDispatch shows the gain.

Measurements

Setup:

  • MSBench hello benchmark on Windows and Linux, using Insiders ec2e580 with this change's bundle.
  • 4 runs per arm, with the first turn and a follow-up turn measured separately.
  • "VS Code repo" means the workspace was a checkout of the VS Code repo (about 19.6K files).
  • Values are medians (off → on).
Scenario Checkpoint wait removed Turn start → handoff to Copilot Turn start → first model call Time to first progress (UI)
Windows, small workspace, follow-up turn 221 ms 248 → 32 ms (−87%) 444 → 296 ms (−33%) 2290 → 2054 ms (−10%)
Windows, VS Code repo, follow-up turn* 403 ms 464 → 50 ms (−89%) 688 → 476 ms (−31%) 2322 → 2284 ms (−2%)
Windows, VS Code repo, first turn 512 ms 1079 → 524 ms (−51%) 1810 → 1704 ms (−6%) 6008 → 6710 ms (+12%)†
Windows, small workspace, first turn 60 ms 469 → 364 ms (−22%) 1076 → 1098 ms (+2%) 4905 → 4880 ms (−1%)
Linux, VS Code repo, follow-up turn* 198 ms 269 → 82 ms (−70%) 418 → 281 ms (−33%) 1944 → 2095 ms (+8%)†
Linux, VS Code repo, first turn 96 ms 644 → 415 ms (−36%) 1694 → 1115 ms (−34%) 6478 → 5514 ms (−15%)†

* Leaves out runs where session.send() was held for up to 1.3 s by a github-mcp-server auth retry loop. The benchmark has no GitHub MCP auth, and the loop happened in both arms. It's tracked on the runtime side in github/copilot-agent-runtime#24238.

† Within run-to-run noise. Time to first progress (UI) includes the model's own response time, which varies by about ±1 s between runs, so at n=4 differences under ~15% aren't meaningful. The "first model call" column is the reliable measure of this change, and the experiment will give the real UI number.

Notes:

  • Windows follow-up turns are about 10× more common than first turns in production, so the follow-up rows are where most of the user-visible gain is.
  • Time to first progress (UI) includes the model's own response time, so at n=4 only the Windows small-workspace follow-up shows a gain clearly above noise. The experiment will give the real number.
  • With the checkpoint taken off the critical path, Copilot's own session setup accounts for most of the remaining first-turn time.

How to test

  1. Set "chat.agentHost.experimental.deferTurnStartCheckpoint": true and use an agent-host (Copilot CLI) session in a git workspace.
  2. Send a message that edits a file, then a follow-up. In agenthost.log, sendMessage called / session.send() returned appear before git write-tree finishes.
  3. Confirm the edit isn't applied until the capture finishes, and that checkpoint restore and undo still work.
  4. Optionally, install a plugin with a SessionStart or UserPromptSubmit hook command and confirm it runs only after the capture finishes.

…experiment)

Behind the experiment-controlled setting
chat.agentHost.experimental.deferTurnStartCheckpoint (default off), a turn
is sent to a provider that sets IAgentChats.supportsTurnStartBarrier while
its turn-start checkpoint is still being captured. The pending capture is
passed as IAgentChatContext.turnStartBarrier, and Copilot holds every tool in
its onPreToolUse hook until it settles, so tools still cannot modify the
working tree before the checkpoint describes it. Providers without the flag
keep waiting before dispatch.

Checkpoint readers (getBaselineCheckpoint, getTurnCheckpointPair) wait for a
session's in-flight turn-start captures, since a session's first capture also
creates its baseline. A failed deferred capture is logged and the turn
continues. The experiment trigger fires in both arms once a turn with a
checkpoint reaches a provider that supports the barrier.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 1, 2026 23:16

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.

Copilot review overview

🟡 Changes recommended

Mutation-capable prompt and session hooks can bypass the barrier and race checkpoint capture.

Review effort: Balanced
Findings: 1 High severity · 1 Low severity

Open (2)
What changed in this PR

Parallelizes turn dispatch with checkpoint capture behind an experimental setting while preserving tool-time checkpoint barriers.

Changes:

  • Adds provider barrier capability and Copilot enforcement.
  • Makes checkpoint readers await pending captures.
  • Adds configuration, telemetry documentation, and tests.
File Description
common/​agent.ts Defines barrier contracts.
common/​agentHostSchema.ts Adds host configuration schema.
common/​agentHostStarter.config.contribution.ts Registers the experiment setting.
common/​agentService.ts Defines the setting identifier.
node/​agentSideEffects.ts Defers checkpoint waiting and reports exposure.
node/​agentHostCheckpointService.ts Tracks pending captures for readers.
node/​copilot/​copilotAgent.ts Advertises and forwards barrier support.
node/​copilot/​copilotAgentSession.ts Blocks tool execution on the barrier.
OTEL.md Documents checkpoint timing behavior.
test/​node/​agentSideEffects.test.ts Tests deferred dispatch and failure behavior.
test/​node/​agentHostCheckpointService.test.ts Tests baseline-reader synchronization.
test/​node/​copilotAgentSession.test.ts Tests tool barrier enforcement.

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/vs/platform/agentHost/node/copilot/copilotAgent.ts
Comment thread src/vs/platform/agentHost/OTEL.md Outdated
@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Base: c466f3cd Current: 2dd0932b

No screenshot changes.

Plugin hook commands (SessionStart, UserPromptSubmit, ErrorOccurred, ...) can modify the working tree, so they now wait for the turn-start checkpoint like tools do. The barrier is also handed to prepareTurn and tracked per chat, so hooks that run while a session starts (before send installs the turn's barrier) wait too. Clarify when sendStageCheckpointMs is absent.

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

This branch has not been deployed

No deployments
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