Skip to content

agentHost (ssh): commit-pin remote CLI on fresh start#318496

Draft
roblourens wants to merge 2 commits into
mainfrom
agents/vsckb-plan-so-looking-at-how-we-connect-d2da6178
Draft

agentHost (ssh): commit-pin remote CLI on fresh start#318496
roblourens wants to merge 2 commits into
mainfrom
agents/vsckb-plan-so-looking-at-how-we-connect-d2da6178

Conversation

@roblourens
Copy link
Copy Markdown
Member

What

Mirrors Remote-SSH's exec-server installer for the agent-host CLI:

  • Install at ~/<serverDataFolderName>/<archive>-<commit> (shared with Remote-SSH).
  • Reuse on file existence — pure test -x, no --version re-parse.
  • On miss, download the commit-pinned artifact (update.code.visualstudio.com/commit:<sha>/cli-<os>-<arch>/<quality>), extract into a temp dir, atomic-mv into the commit-keyed path.
  • Prune old commit-keyed CLIs (keep last 5 by mtime).

Every fresh agent-host start now pushes the CLI that matches the desktop's commit, so users stop ending up with a stale CLI when the supervisor never self-updates.

Why

The agent-host CLI on the remote is supposed to be kept fresh, but on the SSH-launched-AH path it never self-updates — code update is a manual subcommand, and the only auto self-update in the CLI lives in the tunnel path (cli/src/tunnels/control_server.rs::handle_update, gated on params.do_update). cli/src/tunnels/agent_host.rs::run_update_loop only refreshes the server commit cache, never the CLI binary. Net result: users keep ending up with an old CLI.

Easiest fix: have the desktop push the matching CLI on every fresh start, the same way Remote-SSH does for the exec-server.

Differences from Remote-SSH

Softer fallback. Agent host has no strict commit-lock with the desktop (the renderer↔AH protocol handshake catches real incompatibilities), so when the commit-pinned download fails we reuse any other usable CLI on the box rather than refusing to connect:

  1. newest commit-keyed binary in the shared install root, then
  2. legacy ~/.vscode-cli{,-<quality>}/<archive> from the previous installer.

Each candidate is --version-validated; first one that works wins, with a warning logged.

Scope

  • A live agent host found via the lockfile is still reused as-is. The install only runs on AH cold-start, so existing connections are untouched.
  • Dev/OSS builds with no productService.commit keep the old non-pinned loose-install behaviour (<root>/<archive> + latest URL + --version-based reuse), with a warning logged so it's obvious in dev.
  • remoteAgentHostCommand override skips install entirely (unchanged).

Files

  • src/vs/platform/agentHost/node/sshRemoteAgentHostHelpers.ts — new helpers (getRemoteCLIArchiveName, getRemoteCLIInstallRoot, commit-aware getRemoteCLIBin, commit-aware buildCLIDownloadUrl, buildCleanupOldCLIsCommand, buildFindFallbackCLICommand).
  • src/vs/platform/agentHost/node/sshRemoteAgentHostService.ts_ensureCLIInstalled rewritten; dispatches to pinned vs loose paths; soft fallback. _startRemoteAgentHost now takes cliBin: string | undefined instead of quality.
  • Tests updated and extended; 73 helper tests + 61 service tests pass (incl. 5 new commit-pinned install tests covering cleanup, reuse, commit-pinned URL, soft fallback, and no-fallback error propagation).

(Written by Copilot)

Mirrors Remote-SSH's exec-server installer for the agent-host CLI: install
at ~/<serverDataFolderName>/<archive>-<commit> (shared with Remote-SSH),
reuse on file existence, download the commit-pinned artifact on miss, and
prune old commit-keyed CLIs (keep last 5). On every fresh start we push the
CLI that matches the desktop's commit, so users stop ending up with a stale
CLI when the supervisor never self-updates.

Softer fallback than Remote-SSH: agent host has no strict commit-lock with
the desktop, so when the commit-pinned download fails we reuse any other
usable CLI on the box (other commit-keyed binary or legacy ~/.vscode-cli{,-<quality>}/<archive>)
rather than refusing to connect. The renderer<->AH handshake catches real
incompatibilities.

A live agent host found via the lockfile is still reused as-is; the install
only runs on AH cold-start. Dev/OSS builds with no productService.commit
keep the old non-pinned loose-install behaviour.

(Written by Copilot)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 27, 2026 03:05
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 updates the SSH-launched agent-host flow to keep the remote VS Code CLI in sync with the desktop build by installing a commit-pinned CLI binary (when productService.commit is available), reusing it by file existence, pruning older commit-keyed installs, and introducing a soft fallback to any other usable CLI if the pinned download fails.

Changes:

  • Add helper functions to derive commit-aware remote CLI paths, build commit-pinned download URLs, and generate shell snippets for pruning and fallback discovery.
  • Rewrite the SSH remote agent host service’s CLI resolution/installation to prefer commit-pinned installs with tolerant fallback behavior.
  • Extend/adjust unit tests to cover commit-pinned install, reuse, cleanup, and fallback scenarios.
Show a summary per file
File Description
src/vs/platform/agentHost/node/sshRemoteAgentHostHelpers.ts Adds commit-aware CLI naming/path helpers and shell snippet builders for cleanup and fallback search.
src/vs/platform/agentHost/node/sshRemoteAgentHostService.ts Reworks CLI installation/selection to support commit-pinned installs, cleanup, and fallback; updates start path to use a resolved CLI binary path.
src/vs/platform/agentHost/test/node/sshRemoteAgentHostHelpers.test.ts Adds tests for new helpers (archive naming, install root/bin path, commit-pinned URL, cleanup/fallback commands).
src/vs/platform/agentHost/test/node/sshRemoteAgentHostService.test.ts Adds commit-pinned install flow tests (cleanup invocation, reuse, pinned download URL, soft fallback, no-fallback error).

Copilot's findings

  • Files reviewed: 4/4 changed files
  • Comments generated: 4

Comment on lines +139 to +143
const commitGlob = '?'.repeat(40);
// `ls -1t` sorts by mtime newest-first across both Linux (coreutils) and
// macOS (BSD). `awk 'NR>5'` drops the 5 most recent entries we want to
// keep. `xargs rm -f` ignores missing files; the leading `2>/dev/null`
// suppresses "No such file" complaints when the directory is empty.
Comment on lines +160 to +164
const archive = getRemoteCLIArchiveName(quality);
const commitGlob = '?'.repeat(40);
const q = validateShellToken(quality, 'quality');
const legacyDir = q === 'stable' ? '~/.vscode-cli' : `~/.vscode-cli-${q}`;
const legacyBin = `${legacyDir}/${archive}`;
Comment on lines 723 to 726
// 3. Install CLI if needed
reportProgress(localize('sshProgressInstallingCLI', "Checking remote CLI installation..."));
await this._ensureCLIInstalled(sshClient, platform, reportProgress);
cliBin = await this._ensureCLIInstalled(sshClient, platform, reportProgress);
}
Comment on lines +1399 to +1403
// Prune stale commit-keyed CLIs before the reuse check so that a long-
// running session that keeps cycling through desktop builds doesn't
// accumulate binaries forever. Best-effort: cleanup failures are
// harmless and ignored.
await sshExec(client, buildCleanupOldCLIsCommand(this._serverDataFolderName, this._quality), { ignoreExitCode: true });
Four fixes from the PR review (#318496):

- Lazy CLI install: skip uname/install entirely when a running agent
  host is reused via the lockfile. Install only runs immediately before
  we spawn a fresh agent host (introduces an ensureCliResolved closure
  in connect()).
- Cleanup-after-reuse in _ensureCLIInstalledPinned: test -x first, then
  touch the in-use binary so the keep-newest-5 prune cannot evict the
  CLI we're about to run.
- Hex-only glob ([0-9a-f]{40}) in buildCleanupOldCLIsCommand and
  buildFindFallbackCLICommand, plus safer xargs -I{} rm -f -- form.
- Validate _findFallbackCLI candidates through a new exported
  isValidFallbackCLIPath helper before invoking them; reject anything
  that isn't a commit-keyed path under the install root or a legacy
  ~/.vscode-cli{,-<quality>}/<archive> path.

Tests:
- New isValidFallbackCLIPath suite (7 cases).
- New AH-reuse regression test asserting uname/test-x/--version/curl
  never run on the reuse path.
- All affected service-test execResponses arrays reshaped for the new
  ordering; 67 service tests and 81 helper tests passing.

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