You said:
ChatGPT said:
I’ll diff the package manifests/file trees first, then isolate the changed bundles and de-minify only the parts that actually moved. I’ll also distinguish “real behavior changes” from build/version churn so the result is useful rather than a giant textual diff.
The package-level diff is tiny: no new commands/settings/contributions. The conspicuous addition is VS Code’s proposed terminalRemoteResolver API; the actual bundles grew by only ~3.7 KB each, which strongly suggests one focused feature rather than a rewrite. I’m tracing that proposal through extension.js and resolver.js now.
The formatter install hit the environment’s network timeout, but that’s not blocking the analysis. These are webpack bundles with stable numeric module boundaries, so I can compare module-by-module directly—actually cleaner here because it avoids formatter-induced noise.
Yep — I cracked both archives open and compared the webpack bundles module-by-module. There is essentially one real Remote-SSH feature/fix in this update; the rest is dependency and packaging churn.
The actual Remote-SSH change
0.125.2026081318 adopts VS Code's new proposed terminalRemoteResolver API. The extension now marks the hidden terminal used to bootstrap/install the remote VS Code server with isRemoteResolverTerminal: true.
The flag is threaded through exactly this path:
tryInstall → runSshTerminalCommandWithLogin → runInteractiveSshTerminalCommand → vscode.window.createTerminal(...)
Only the "install" bootstrap terminal sets it to true. Normal terminals are unchanged.
This fixes a fairly nasty circular dependency involving Workspace Trust. When opening an SSH workspace through Open Recent, VS Code could be in Restricted Mode and wait for workspace trust before allowing the local bootstrap terminal to start—but establishing the remote connection itself was necessary to finish opening the workspace. The result was the apparently nonsensical SSH timeout reported in microsoft/vscode#328475. GitHub
VS Code PR #329228 describes exactly the mechanism I found in your bundle: the special resolver terminal may bypass only the terminal process's workspace-trust wait; it does not mark the workspace trusted, and ordinary terminals retain the existing trust behavior. The PR even gives Remote-SSH adoption instructions that match the bundle almost verbatim. GitHub+1
The new archive consequently contains vscode.proposed.terminalRemoteResolver.d.ts, adds terminalRemoteResolver to enabledApiProposals, and conditionally adds isRemoteResolverTerminal: true to the terminal options.
What else changed
Both archives have 67 files. There are only 6 modified files, 1 removed file, and 1 added file. extension.js grew ~3.7 KB and resolver.js grew the same amount.
I split both minified webpack bundles on their stable numeric module boundaries. They contain exactly the same module IDs before and after; only 12 module bodies changed in either bundle. Two are the Remote-SSH logic described above, one is the embedded package.json, and the remaining nine are dependency code.
The runtime dependency changes worth noting are:
-
shell-quote^1.8.1→^1.10.0— parser/quoting implementation changed. -
brace-expansion5.0.6 → 5.0.9 — notably, the old bundle defaults to effectively unlimited expansion; the new bundle defaults to 100,000 expansions and 4,000,000 output characters. -
ip-address10.2.0 → 10.4.0 — stricter parsing, including rejecting leading-zero IPv4 octets; new byte-array checks,isHostInSubnet, IPv4-mapped classification, and NAT64 handling. -
minipass7.1.2 → 7.1.3 — tiny stream/error-propagation changes andSymbol.dispose/Symbol.asyncDisposesupport.
There is additional dependency-notice/tooling churn (fast-uri, path-scurry, slash, table, etc.), along with dev-tool updates such as @vscode/vsce ^2.22.0 → ^3.9.2 and copy-webpack-plugin ^11 → ^14.
Crucially, I found no changed Remote-SSH logic for SSH command generation, authentication/password handling, config parsing, tunnels/forwarding, reconnection, server install script contents, settings, commands, activation events, menus, or telemetry.
Packaging changes
This build is now explicitly marked PreRelease in .vsixmanifest. The pre-release warning that used to live in the separate README.preRelease.md is moved into the main README.
There is one compatibility wrinkle: terminalRemoteResolver is a proposal-gated API, so this pre-release is intended for an editor build that knows about that proposal. On August 8 there was already a report where an Insiders/Remote-SSH version mismatch caused "CANNOT use API proposal: terminalRemoteResolver" and prevented SSH remotes from loading. GitHub So I would pair this particular August 13 extension build with a sufficiently recent VS Code Insiders rather than treating it as a drop-in stable build.
So the changelog Microsoft could have written is basically:
Fix Remote-SSH connections getting stuck/timing out when the resolver needs to start its hidden SSH bootstrap terminal before Workspace Trust has been resolved, notably when opening SSH workspaces from Open Recent. Also update dependencies and pre-release packaging.
I saved the more forensic version, including the reconstructed meaningful bundle diffs and dependency details, here: .
You said:
okay, excellent. could you please write a Python patcher (which works at least with the newer version) to fix an annoying issue with the extension? the issue is: when connecting a lot of project windows to the same remote on startup, about half of them fail with CodeError(AuthChallengeBadToken) for reasons unknown. it should just retry in a loop there instead of popping up a dialog. god, Microsoft...
ChatGPT said:
I found the “reasons unknown,” and it’s much better than blindly retrying: the Unix exec-server bootstrap uses one shared file, .cli.${COMMIT_ID}.log, for every window. Each concurrent bootstrap removes/recreates that path, starts its own CLI with its own auth token, then polls that shared pathname for Listening on …. So window A can read window B’s port and then try it with window A’s token → AuthChallengeBadToken.
I’m making the patcher do two things: make that bootstrap log per-process (fixing the race at the source), and add the requested AuthChallengeBadToken retry loop with randomized backoff as a defensive fallback.
Yep. I found a concrete race in the extension that explains the token failures almost perfectly.
The Unix exec-server bootstrap uses the same logfile for every simultaneous connection:
BashCLI_LOG_FILE="${VSCODE_AGENT_FOLDER}/.cli.${COMMIT_ID}.log"
Each window generates its own auth token, starts its own CLI process, and later reads Listening on ... back from that shared pathname. Because concurrent bootstrap processes repeatedly remove/recreate the file, window A can wind up reading window B's port while retaining window A's token. Connecting to B's exec server with A's token produces exactly:
CodeError(AuthChallengeBadToken)
So I made the patcher fix both layers:
-
Fix the race itself by changing the logfile to a per-bootstrap-process path:
BashCLI_LOG_FILE="${VSCODE_AGENT_FOLDER}/.cli.${COMMIT_ID}.$$.log" -
Add the retry loop you asked for. If
h.resolve(...)still throws an error containingAuthChallengeBadToken, it retries the whole resolver operation with randomized backoff instead of allowing the exception to reachhandleResolverFailure()and produce the dialog.
Retrying the whole resolve is important: merely retrying challenge_verify against the same wrong port can never make a mismatched port/token pair correct.
Patcher
It's stdlib-only. With VS Code closed:
Bashpython patch_remote_ssh_auth_retry.py --checkpython patch_remote_ssh_auth_retry.py
With no target, it finds the newest Remote-SSH installation under the usual .vscode/extensions, .vscode-insiders/extensions, or .vscode-oss/extensions directory.
You can also be explicit:
Bashpython patch_remote_ssh_auth_retry.py ~/.vscode/extensions
or patch an archive:
Bashpython patch_remote_ssh_auth_retry.py ms-vscode-remote.remote-ssh-0.125.2026081318.zip
The useful knobs are:
--max-retries N retries after initial failure; 0 = unlimited [default]--min-delay-ms N minimum retry delay [200]--max-delay-ms N backoff cap [2000]--no-log-race-fix only add the retry mechanism--check validate without changing anything--no-backup don't make .bak files for installed-extension patches
By default it retries only AuthChallengeBadToken; unrelated SSH failures still go through Microsoft's normal error handling.
Already-patched 0.125 build
I also ran it against the exact newer archive you uploaded:
I verified that:
-
node --checkaccepts the modifiedextension.js. -
Re-running the patcher detects both modifications as already applied.
-
The archive has the identical file set as the original.
-
Only two files differ in content:
-
out/extension.js -
out/install-script/scripts/linux-exec-server-installer.sh
-
-
The patch also successfully matches and syntax-checks against
0.124.0. -
The patcher refuses to make a fuzzy modification if a future Microsoft bundle no longer has the expected resolver structure.
For reference, SHA-256:
94b76ea824557ba2f6bbe3b912614069c1b356b40d4e7bc298cb3e3ac8f70289 patch_remote_ssh_auth_retry.pydc3bb8ce43fbeb059e6f8ac1af8555f788da650294cc67453620a719d60c0054 ms-vscode-remote.remote-ssh-0.125.2026081318.auth-retry-patched.zip
One practical detail: close/restart all VS Code windows after patching the installed extension. A subsequent Marketplace update will replace Microsoft's files and therefore remove the patch.
You said:
ChatGPT said:
Agreed. For the failure we diagnosed, retry would just mask the race. The cleaner fix is to prevent concurrent resolver sessions from sharing the logfile in the first place.
I stripped the patcher down accordingly:
-
only changes
CLI_LOG_FILE="${VSCODE_AGENT_FOLDER}/.cli.${COMMIT_ID}.log"
→
CLI_LOG_FILE="${VSCODE_AGENT_FOLDER}/.cli.${COMMIT_ID}.$$.log" -
no
extension.jschanges -
no retry loop
-
no ZIP/VSIX handling
-
supports an installed extension root, parent
extensionsdirectory, direct installer path, or auto-discovery -
has
--checkand optional backup suppression -
tested against both
0.124.0and0.125.2026081318
Usage:
Bashpython patch_remote_ssh_cli_log_race.py --checkpython patch_remote_ssh_cli_log_race.py
Or explicitly:
Bashpython patch_remote_ssh_cli_log_race.py ~/.vscode/extensions
It makes an adjacent .bak by default; omit that with:
Bashpython patch_remote_ssh_cli_log_race.py --no-backup
SHA-256:
c1ef25f2428792209cd920d80c088b3b3a0e87f4d450f82dee6159bd7ddc6141
If AuthChallengeBadToken still occurs under the same mass-startup test after this patch, that would be useful evidence that there's a second race elsewhere. I'd investigate that rather than immediately reintroducing generic retries.
You said:
ChatGPT said:
Yep. Updated it so Windows discovery explicitly checks:
-
%USERPROFILE%\.vscode\extensions -
%USERPROFILE%\.vscode-insiders\extensions -
%USERPROFILE%\.vscode-oss\extensions -
%HOMEDRIVE%%HOMEPATH%as a fallback -
Path.home()as the final fallback
So on Windows this should now just work:
PowerShellpython patch_remote_ssh_cli_log_race.py --checkpython patch_remote_ssh_cli_log_race.py
I also re-ran syntax validation and checked it against the extracted 0.125.2026081318 extension.
SHA-256:
106ec0877ac492027b1df97a588565a7aa72bcad62536e7208fb799ef601c1a7
Path.home() normally already lands on %USERPROFILE% under native Windows Python, but explicitly honoring USERPROFILE makes the intended behavior unambiguous and avoids depending on Python's home-resolution details.
You said:
ChatGPT said:
Yep — renamed to avoid whatever attachment caching is happening:
SHA-256 remains:
106ec0877ac492027b1df97a588565a7aa72bcad62536e7208fb799ef601c1a7
The relevant Windows-discovery change is essentially:
Diff+def candidate_home_directories() -> list[Path]:+ candidates: list[Path] = []+ if os.name == "nt":+ user_profile = os.environ.get("USERPROFILE")+ home_drive = os.environ.get("HOMEDRIVE")+ home_path = os.environ.get("HOMEPATH")+ if user_profile:+ candidates.append(Path(user_profile))+ if home_drive and home_path:+ candidates.append(Path(home_drive + home_path))+ else:+ home = os.environ.get("HOME")+ if home:+ candidates.append(Path(home))+ try:+ candidates.append(Path.home())+ except RuntimeError:+ pass+ return unique_paths(candidates)++def discover_root() -> Path:- home = Path.home()roots: list[Path] = []- for relative in DISCOVERY_RELATIVE_DIRS:- directory = home / relative- if directory.is_dir():- roots.extend(roots_under(directory))+ for home in candidate_home_directories():+ for relative in DISCOVERY_RELATIVE_DIRS:+ directory = home / relative+ if directory.is_dir():+ roots.extend(roots_under(directory))return newest_root(roots)
So native Windows explicitly tries %USERPROFILE% first, then %HOMEDRIVE%%HOMEPATH%, then Path.home().