Skip to content

Show progress while downloading agent SDK binaries#322919

Draft
Giuspepe wants to merge 5 commits into
mainfrom
agents/sdk-download-progress-ahp
Draft

Show progress while downloading agent SDK binaries#322919
Giuspepe wants to merge 5 commits into
mainfrom
agents/sdk-download-progress-ahp

Conversation

@Giuspepe

@Giuspepe Giuspepe commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Warning

Not ready to merge — draft. Depends on microsoft/agent-host-protocol#263, which defines the root/downloadProgress wire contract this PR consumes. That PR must merge first. Once it does, the vendored protocol copy here needs a final re-sync from merged AHP main (refreshing src/vs/platform/agentHost/common/state/protocol/.ahp-version to the merged commit) before this comes out of draft.

Screen.Recording.2026-06-25.at.15.19.44.mov

Why

VS Code ships the Claude and Codex agents but not their native binaries — those download on demand from the CDN (70–95 MB compressed) the first time a session of that type starts, then cache. On a cold cache the first turn blocks for several seconds with no feedback (it looks hung). This surfaces a progress indicator in the Agents window instead.

What you see

A toast in the Agents window — "Downloading Claude…" / "Downloading Codex…" — filling 0→100% (determinate when the CDN sends Content-Length, indeterminate otherwise), clearing on completion.

How it works

The download runs in the agent host (utility process locally, or the remote server over WSL/SSH); the UI is in the renderer. Progress crosses the agent-host protocol via the new root/downloadProgress notification:

  1. agentSdkDownloader._fetch counts received bytes vs Content-Length, throttles, and fires onDidDownloadProgress.
  2. The local/remote bridges map it onto the generic AHP frame (kind: 'agent-sdk', resourceId, phase) through the state manager — the same fan-out path as session notifications, so it reaches both local (IPC) and remote (WebSocket) clients.
  3. The Agents window renders it as an IProgressService toast keyed by downloadId.

Also gates Codex's eager prewarm-on-create so it no longer triggers a cold download with no user action — the download now happens on first message.

Depends on

Vendors the protocol types from microsoft/agent-host-protocol#263, which defines root/downloadProgress. That PR should merge first — it's the source of truth for the wire contract; the vendored copy here (src/vs/platform/agentHost/common/state/protocol/) mirrors it.

Giuspepe and others added 3 commits June 23, 2026 13:32
VS Code ships the Claude and Codex agents but not their native SDK binaries
(70-95 MB), which are downloaded from the CDN the first time a session of that
type starts and then cached. On a cold cache the first session start blocked for
several seconds with no feedback. This surfaces a client-side progress
indicator.

How it works:
- The downloader counts bytes in `_fetch` (using Content-Length when present),
  and fires a process-global `onDidDownloadProgress` event with throttled
  lifecycle frames (started -> progress -> completed/failed). Callers stay
  ignorant of progress.
- The agent host forwards that event into
  `AgentHostStateManager.emitSdkDownloadProgress`, which emits the new
  `root/sdkDownloadProgress` AHP notification on the same `onDidEmitNotification`
  path used by session notifications. That reaches BOTH the local Agents window
  (IPC proxy) and remote hosts (WebSocket ProtocolServerHandler) with no
  transport-specific special casing.
- `BaseAgentHostSessionsProvider` renders an `IProgressService` notification
  keyed by `downloadId` (determinate % when the total is known, MB otherwise),
  gated on `chat.disableAIFeatures`.

Also gates the eager `listSessions` path (new
`isSdkResolvableWithoutDownload` / `canLoadWithoutDownload`) so a cold download
is triggered by the user's first message, not at agent-host startup.

The protocol files mirror agent-host-protocol's `root/sdkDownloadProgress`
addition (unreleased spec 0.5.0).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Codex eagerly prewarms a session on `createSession` (materializing the thread so
the first turn is instant), which spawns the native binary and therefore loads
the SDK. When the SDK isn't cached yet, that prewarm kicked off a multi-second
cold download the moment the user landed on a Codex new-session composer —
before sending any message.

Gate `_schedulePrewarm` on the same `isSdkResolvableWithoutDownload` check
already used for `listSessions`: when the SDK isn't local, skip prewarm and let
the first `sendMessage` materialize the thread and fire the (progress-reported)
download. This brings Codex in line with Claude, which already loads the SDK
lazily on first use rather than on session creation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Rename the protocol notification from the SDK-specific `root/sdkDownloadProgress`
to a resource-agnostic `root/downloadProgress`, so the same channel can report
future host-side downloads (additional agent runtimes, plugins, models, …)
without a new method. Done now while the notification is still in the unreleased
0.5.0 spec, so it costs nothing; renaming a shipped wire method would otherwise
mean a deprecation cycle across the generated language clients.

Protocol changes (mirrors agent-host-protocol):
- `SdkDownloadProgressParams` → `DownloadProgressParams`.
- `SdkDownloadPhase` → `DownloadPhase`, now a string-valued `const enum` to match
  every other protocol discriminant (and so the AHP code generators emit it).
- Add a `kind` discriminant (open string, `'agent-sdk'` today) and rename
  `packageId` → `resourceId`.

The vscode SDK downloader stays SDK-specific (it only downloads SDKs) and keeps
its own `AgentSdkDownloadPhase` string union; the agent-host bridges map its
event onto the generic frame, stamping `kind: 'agent-sdk'` and translating the
phase to the protocol `DownloadPhase` enum. The state manager method is now
`emitDownloadProgress`, and the renderer uses `kind` to pick a
resource-appropriate label ("Downloading {name} agent…" for agent SDKs,
"Downloading {name}…" otherwise).

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

# Conflicts:
#	src/vs/platform/agentHost/common/state/protocol/.ahp-version
#	src/vs/platform/agentHost/node/claude/claudeAgentSdkService.ts
#	src/vs/sessions/contrib/providers/agentHost/browser/baseAgentHostSessionsProvider.ts

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

This PR adds end-to-end download progress reporting for on-demand agent SDK binary downloads (Claude/Codex), forwarding progress from the agent host through the Agent Host Protocol (root/downloadProgress) and surfacing it in the Sessions (Agents) window as an IProgressService notification.

Changes:

  • Emit throttled byte/total download progress from AgentSdkDownloader and forward it via AgentHostStateManager.emitDownloadProgress() onto the AHP root notification channel.
  • Render root/downloadProgress notifications in the Sessions providers as notification progress toasts keyed by downloadId.
  • Avoid cold-download side effects from non-user-initiated Codex/Claude operations (e.g. prewarm / listSessions) by gating them on “SDK already local” checks; add/extend unit tests for protocol forwarding and downloader progress.
Show a summary per file
File Description
src/vs/sessions/contrib/providers/remoteAgentHost/test/browser/remoteAgentHostSessionsProvider.test.ts Stubs IProgressService for provider tests after adding progress dependency.
src/vs/sessions/contrib/providers/remoteAgentHost/browser/remoteAgentHostSessionsProvider.ts Plumbs IProgressService into the remote sessions provider base constructor.
src/vs/sessions/contrib/providers/agentHost/test/browser/localAgentHostSessionsProvider.test.ts Stubs IProgressService for provider tests after adding progress dependency.
src/vs/sessions/contrib/providers/agentHost/browser/localAgentHostSessionsProvider.ts Plumbs IProgressService into the local sessions provider base constructor.
src/vs/sessions/contrib/providers/agentHost/browser/baseAgentHostSessionsProvider.ts Tracks active downloads and renders root/downloadProgress as notification progress toasts.
src/vs/platform/agentHost/test/node/protocolServerHandler.test.ts Adds coverage verifying root/downloadProgress frames are forwarded to connected clients.
src/vs/platform/agentHost/test/node/claudeSubagentResolver.test.ts Updates fake Claude SDK service to satisfy new canLoadWithoutDownload() API.
src/vs/platform/agentHost/test/node/claudeAgent.test.ts Updates fakes/stubs for new downloader + Claude SDK service APIs.
src/vs/platform/agentHost/test/node/claudeAgent.integrationTest.ts Updates integration fake to satisfy new canLoadWithoutDownload() API.
src/vs/platform/agentHost/test/node/agentSdkDownloader.test.ts Adds tests validating monotonic progress frames and terminal completion semantics.
src/vs/platform/agentHost/node/codex/codexAgent.ts Prevents prewarm/listing from triggering cold SDK downloads; adds displayName.
src/vs/platform/agentHost/node/claude/claudeAgentSdkService.ts Adds displayName and canLoadWithoutDownload() to gate session listing.
src/vs/platform/agentHost/node/claude/claudeAgent.ts Gates listSessions() to avoid cold downloads at startup.
src/vs/platform/agentHost/node/agentSdkDownloader.ts Implements progress eventing + “resolvable without download” checks; adds display name to packages.
src/vs/platform/agentHost/node/agentHostStateManager.ts Adds emitDownloadProgress() on the root notification channel.
src/vs/platform/agentHost/node/agentHostServerMain.ts Wires SDK download progress events into root/downloadProgress notifications.
src/vs/platform/agentHost/node/agentHostMain.ts Wires SDK download progress events into root/downloadProgress notifications.
src/vs/platform/agentHost/common/state/sessionActions.ts Exposes DownloadPhase / DownloadProgressParams and adds NotificationType.DownloadProgress.
src/vs/platform/agentHost/common/state/protocol/version/registry.ts Declares root/downloadProgress as introduced in protocol 0.5.0.
src/vs/platform/agentHost/common/state/protocol/common/messages.ts Adds root/downloadProgress to the server notification map.
src/vs/platform/agentHost/common/state/protocol/channels-root/notifications.ts Defines the root/downloadProgress wire contract (phase/bytes/ids).
src/vs/platform/agentHost/common/state/protocol/.ahp-version Updates vendored protocol revision pointer.
src/vs/platform/agentHost/browser/remoteAgentHostProtocolClient.ts Allows root/downloadProgress notifications through the client switch.

Copilot's findings

  • Files reviewed: 23/23 changed files
  • Comments generated: 2

Comment thread src/vs/platform/agentHost/node/agentHostMain.ts
Comment thread src/vs/platform/agentHost/node/agentHostServerMain.ts
AgentSdkDownloader extends Disposable and owns a registered Emitter
(onDidDownloadProgress), but both construction sites created it via
createInstance(...) without adding it to the function's DisposableStore —
unlike every sibling service. That left its Emitter undisposed for the
process lifetime. Wrap both in disposables.add(...) to match the pattern.

Addresses Copilot code-review feedback on the PR.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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