Skip to content

agentHost: auto-connect for tunnels and cached sessions#311472

Merged
connor4312 merged 2 commits intomainfrom
connor4312/ah-auto-connect
Apr 20, 2026
Merged

agentHost: auto-connect for tunnels and cached sessions#311472
connor4312 merged 2 commits intomainfrom
connor4312/ah-auto-connect

Conversation

@connor4312
Copy link
Copy Markdown
Member

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.

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.
Copilot AI review requested due to automatic review settings April 20, 2026 20:02
@connor4312 connor4312 self-assigned this Apr 20, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Screenshot Changes

Base: a557dbcf Current: a8fb8d89

Changed (2)

chat/aiCustomizations/aiCustomizationManagementEditor/McpBrowseMode/Light
Before After
before after
editor/inlineChatAffordance/InlineChatOverlay/Light
Before After
before after

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 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 (default true) 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 _unpublished back to false and refreshes. Because the sessions are still present in _sessionCache, _refreshSessions() will typically emit only changed events (not added), 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 an added event 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() uses getObject(...) on load but stores JSON.stringify(limited) on save. IStorageService.store supports storing objects/arrays directly (and getObject will round-trip them), so stringifying here is redundant and makes the stored shape less self-describing. Prefer storing limited as 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 to Connected/Disconnected based on hostConnectionCount, but the final _updateConnectionStatuses() call will immediately overwrite that with Disconnected for any tunnel that doesn't have a live IRemoteAgentHostService entry (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 from IStorageService, (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

Comment thread src/vs/sessions/contrib/chat/browser/sessionWorkspacePicker.ts
@connor4312 connor4312 marked this pull request as ready for review April 20, 2026 20:47
@connor4312 connor4312 enabled auto-merge (squash) April 20, 2026 20:47
@connor4312 connor4312 merged commit 4fd4618 into main Apr 20, 2026
26 checks passed
@connor4312 connor4312 deleted the connor4312/ah-auto-connect branch April 20, 2026 21:17
@vs-code-engineering vs-code-engineering bot added this to the 1.118.0 milestone Apr 20, 2026
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.

3 participants