agentHost: auto-connect for tunnels and cached sessions#311472
Merged
connor4312 merged 2 commits intomainfrom Apr 20, 2026
Merged
agentHost: auto-connect for tunnels and cached sessions#311472connor4312 merged 2 commits intomainfrom
connor4312 merged 2 commits intomainfrom
Conversation
Persist cached session summaries per remote agent host and publish them
immediately on startup so the workspace picker and sessions list surface
prior work without waiting for a live connection. On load, attempt to
reach any online tunnels and configured SSH hosts automatically, gated
behind a new chat.remoteAgentHostsAutoConnect setting (default on). If a
host is unreachable, stop publishing its cached sessions for the current
window while leaving the cache + storage intact, so the sessions
reappear once the host comes back online (or on next launch).
Improvements to the workspace picker:
- Show tunnels as "Connecting..." initially instead of briefly flashing
"Offline" before the first silent status check completes.
- Render offline tunnels as disabled with a refresh toolbar button
("Attempt to Connect") that retries the connection, matching the
existing "Options" affordance for SSH hosts.
- Clicking the refresh button runs the provider's browse action, which
invokes connectOnDemand for that specific tunnel and surfaces a
visible error notification on failure instead of silently doing
nothing.
Contributor
Contributor
There was a problem hiding this comment.
Pull request overview
This PR improves the remote agent host experience in the Sessions window by persisting cached remote session summaries per host and proactively attempting to connect to known tunnel/SSH hosts on startup (behind a new auto-connect setting), so the workspace picker and sessions list can surface prior work sooner.
Changes:
- Persist and load cached remote session summaries per remote agent host, with an “unpublish” mechanism to hide cached sessions when a host is deemed unreachable.
- Add
chat.remoteAgentHostsAutoConnect(defaulttrue) and gate tunnel/SSH startup auto-connect behavior on it. - Update the workspace picker UX for tunnels (initial “Connecting…”, offline disabled state, and an “Attempt to Connect” toolbar action that triggers the provider browse/connect flow).
Show a summary per file
| File | Description |
|---|---|
| src/vs/sessions/contrib/remoteAgentHost/browser/tunnelAgentHost.contribution.ts | Adds initial “Connecting” state, auto-connect gating, and unpublishing of cached sessions for offline tunnels. |
| src/vs/sessions/contrib/remoteAgentHost/browser/remoteAgentHostSessionsProvider.ts | Implements persisted cached session summaries + “unpublish cached sessions” behavior and improved connect-on-demand error surfacing. |
| src/vs/sessions/contrib/remoteAgentHost/browser/remoteAgentHost.contribution.ts | Adds auto-connect setting and gates SSH reconnect attempts; unpublishes cached sessions on SSH reconnect failure. |
| src/vs/sessions/contrib/chat/browser/sessionWorkspacePicker.ts | Updates remote provider items: tunnels disabled when offline + refresh toolbar action; selection behavior adjusted. |
| src/vs/sessions/contrib/agentHost/browser/baseAgentHostSessionsProvider.ts | Import reordering only. |
| src/vs/platform/agentHost/common/remoteAgentHostService.ts | Introduces RemoteAgentHostAutoConnectSettingId constant. |
Copilot's findings
Comments suppressed due to low confidence (4)
src/vs/sessions/contrib/remoteAgentHost/browser/remoteAgentHostSessionsProvider.ts:365
- After
unpublishCachedSessions()hides sessions,setConnection()just flips_unpublishedback tofalseand refreshes. Because the sessions are still present in_sessionCache,_refreshSessions()will typically emit onlychangedevents (notadded), so consumers that removed them will not repopulate the list. If cached sessions should reappear when the host becomes reachable again,setConnection()(or a dedicated publish method) needs to explicitly re-announce cached sessions (e.g. fire anaddedevent for the current cache) before/alongside refresh.
setConnection(connection: IAgentConnection, defaultDirectory?: string): void {
if (this._connection === connection && this._defaultDirectory === defaultDirectory) {
return;
}
this._connectionListeners.clear();
this._sessionStateSubscriptions.clearAndDisposeAll();
this._connection = connection;
this._defaultDirectory = defaultDirectory;
this._unpublished = false;
// Dynamically discover session types from the host's advertised agents.
const rootStateValue = connection.rootState.value;
if (rootStateValue && !(rootStateValue instanceof Error)) {
this._syncSessionTypesFromRootState(rootStateValue);
}
this._connectionListeners.add(connection.rootState.onDidChange(rootState => {
this._syncSessionTypesFromRootState(rootState);
}));
this._attachConnectionListeners(connection, this._connectionListeners);
// Always refresh sessions when a connection is (re)established
this._cacheInitialized = true;
this._refreshSessions();
}
src/vs/sessions/contrib/remoteAgentHost/browser/remoteAgentHostSessionsProvider.ts:478
_persistCache()usesgetObject(...)on load but storesJSON.stringify(limited)on save.IStorageService.storesupports storing objects/arrays directly (andgetObjectwill round-trip them), so stringifying here is redundant and makes the stored shape less self-describing. Prefer storinglimitedas an object/array directly so load/save are symmetric and avoid extra serialization/parsing work.
if (entries.length === 0) {
this._storageService.remove(this._storageKey, StorageScope.APPLICATION);
return;
}
entries.sort((a, b) => b.modifiedTime - a.modifiedTime);
const limited = entries.slice(0, CACHED_SESSIONS_MAX_PER_HOST);
this._storageService.store(this._storageKey, JSON.stringify(limited), StorageScope.APPLICATION, StorageTarget.USER);
}
src/vs/sessions/contrib/remoteAgentHost/browser/tunnelAgentHost.contribution.ts:210
_silentStatusCheck()sets tunnel providers toConnected/Disconnectedbased onhostConnectionCount, but the final_updateConnectionStatuses()call will immediately overwrite that withDisconnectedfor any tunnel that doesn't have a liveIRemoteAgentHostServiceentry (i.e. online-but-not-relay-connected tunnels). This effectively removes the “online but not connected” state and will make the picker show tunnels as Offline unless a relay connection is already established._updateConnectionStatuses()should incorporate the last known tunnel-online status (or avoid overriding it when there is no live connection/pending connect).
private _updateConnectionStatuses(): void {
for (const [address, provider] of this._providerInstances) {
const connectionInfo = this._remoteAgentHostService.connections.find(c => c.address === address);
if (connectionInfo) {
provider.setConnectionStatus(connectionInfo.status);
} else if (this._pendingConnects.has(address)) {
provider.setConnectionStatus(RemoteAgentHostConnectionStatus.Connecting);
} else if (!this._initialStatusChecked) {
// Keep the initial "Connecting" state so the picker doesn't
// flash "Offline" before the first silent status check runs.
provider.setConnectionStatus(RemoteAgentHostConnectionStatus.Connecting);
} else {
provider.setConnectionStatus(RemoteAgentHostConnectionStatus.Disconnected);
}
}
src/vs/sessions/contrib/remoteAgentHost/browser/remoteAgentHostSessionsProvider.ts:447
- New persisted-session caching and publish/unpublish behavior is being introduced here, but there are existing unit tests for this provider (
src/vs/sessions/contrib/remoteAgentHost/test/browser/remoteAgentHostSessionsProvider.test.ts) and the new persistence/unpublish flows aren’t covered. Please add tests for (1) loading cached sessions fromIStorageService, (2) persisting updates, and (3)unpublishCachedSessions()not clearing the persisted cache and sessions being re-published when the host reconnects.
/** Load persisted session summaries into {@link _sessionCache}. */
private _loadCachedSessions(): void {
const parsed = this._storageService.getObject(this._storageKey, StorageScope.APPLICATION);
if (!Array.isArray(parsed)) {
return;
}
for (const entry of parsed as readonly ISerializedSessionMetadata[]) {
const meta = deserializeMetadata(entry);
if (!meta) {
continue;
}
const rawId = AgentSession.id(meta.session);
if (this._sessionCache.has(rawId)) {
continue;
}
const cached = this.createAdapter(meta);
this._sessionCache.set(rawId, cached);
}
}
- Files reviewed: 6/6 changed files
- Comments generated: 2
DonJayamanne
approved these changes
Apr 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Persist cached session summaries per remote agent host and publish them immediately on startup so the workspace picker and sessions list surface prior work without waiting for a live connection. On load, attempt to reach any online tunnels and configured SSH hosts automatically, gated behind a new chat.remoteAgentHostsAutoConnect setting (default on). If a host is unreachable, stop publishing its cached sessions for the current window while leaving the cache + storage intact, so the sessions reappear once the host comes back online (or on next launch).
Improvements to the workspace picker: