From bd980b37210aa1a3f342eef967e36c8e54246697 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 25 May 2026 17:34:46 -0700 Subject: [PATCH 1/2] Preserve unread state across remote host disconnect Avoid reporting cached remote agent host sessions as removed when a host is temporarily unpublished during disconnect/reconnect. Re-announce cached sessions when the host reconnects so the sessions list refreshes without treating the outage as deletion. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/vs/sessions/SESSIONS_LIST.md | 1 + .../browser/baseAgentHostSessionsProvider.ts | 9 +++++++-- .../browser/remoteAgentHostSessionsProvider.ts | 8 ++++---- .../remoteAgentHostSessionsProvider.test.ts | 17 ++++++++++++++--- 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/vs/sessions/SESSIONS_LIST.md b/src/vs/sessions/SESSIONS_LIST.md index 9f9f54485345c..09d5f7c92eaf8 100644 --- a/src/vs/sessions/SESSIONS_LIST.md +++ b/src/vs/sessions/SESSIONS_LIST.md @@ -90,6 +90,7 @@ Pinned sessions appear in a dedicated "Pinned" section at the top. Pin state is - Sessions start as **unread** - A session becomes **read** when the user opens it or explicitly marks it - A session becomes **unread** when it completes in the background (transitions from InProgress to a terminal status while not active) +- Pin and read state is cleaned up when a provider reports a real session removal; remote agent host disconnects hide cached sessions without reporting them as removed ### Navigation diff --git a/src/vs/sessions/contrib/providers/agentHost/browser/baseAgentHostSessionsProvider.ts b/src/vs/sessions/contrib/providers/agentHost/browser/baseAgentHostSessionsProvider.ts index c32aa73b4430b..15a488e23d944 100644 --- a/src/vs/sessions/contrib/providers/agentHost/browser/baseAgentHostSessionsProvider.ts +++ b/src/vs/sessions/contrib/providers/agentHost/browser/baseAgentHostSessionsProvider.ts @@ -2035,7 +2035,7 @@ export abstract class BaseAgentHostSessionsProvider extends Disposable implement this._refreshSessions(); } - protected async _refreshSessions(): Promise { + protected async _refreshSessions(announceExistingAsAdded = false): Promise { const connection = this.connection; if (!connection) { return; @@ -2052,8 +2052,13 @@ export abstract class BaseAgentHostSessionsProvider extends Disposable implement const existing = this._sessionCache.get(rawId); if (existing) { + if (announceExistingAsAdded) { + added.push(existing); + } if (existing.update(meta)) { - changed.push(existing); + if (!announceExistingAsAdded) { + changed.push(existing); + } } } else { const cached = this.createAdapter(meta); diff --git a/src/vs/sessions/contrib/providers/remoteAgentHost/browser/remoteAgentHostSessionsProvider.ts b/src/vs/sessions/contrib/providers/remoteAgentHost/browser/remoteAgentHostSessionsProvider.ts index 510af4f81dee0..fcb481b46b6b0 100644 --- a/src/vs/sessions/contrib/providers/remoteAgentHost/browser/remoteAgentHostSessionsProvider.ts +++ b/src/vs/sessions/contrib/providers/remoteAgentHost/browser/remoteAgentHostSessionsProvider.ts @@ -375,6 +375,7 @@ export class RemoteAgentHostSessionsProvider extends BaseAgentHostSessionsProvid return; } + const wasUnpublished = this._unpublished; this._connectionListeners.clear(); this._sessionStateSubscriptions.clearAndDisposeAll(); this._connection = connection; @@ -396,7 +397,7 @@ export class RemoteAgentHostSessionsProvider extends BaseAgentHostSessionsProvid // Always refresh sessions when a connection is (re)established this._cacheInitialized = true; - this._refreshSessions(); + this._refreshSessions(wasUnpublished); } /** @@ -450,9 +451,8 @@ export class RemoteAgentHostSessionsProvider extends BaseAgentHostSessionsProvid return; } this._unpublished = true; - const removed: ISession[] = Array.from(this._sessionCache.values()); - if (removed.length > 0) { - this._onDidChangeSessions.fire({ added: [], removed, changed: [] }); + if (this._sessionCache.size > 0) { + this._onDidChangeSessions.fire({ added: [], removed: [], changed: [] }); } } diff --git a/src/vs/sessions/contrib/providers/remoteAgentHost/test/browser/remoteAgentHostSessionsProvider.test.ts b/src/vs/sessions/contrib/providers/remoteAgentHost/test/browser/remoteAgentHostSessionsProvider.test.ts index 6547363e08d03..6d2f87c9619af 100644 --- a/src/vs/sessions/contrib/providers/remoteAgentHost/test/browser/remoteAgentHostSessionsProvider.test.ts +++ b/src/vs/sessions/contrib/providers/remoteAgentHost/test/browser/remoteAgentHostSessionsProvider.test.ts @@ -838,9 +838,10 @@ suite('RemoteAgentHostSessionsProvider', () => { assert.deepStrictEqual( { sessionCount: provider.getSessions().length, + eventCount: events.length, eventRemovedTitles: events.flatMap(e => e.removed.map(s => s.title.get())), }, - { sessionCount: 0, eventRemovedTitles: ['Keep Me'] }, + { sessionCount: 0, eventCount: 1, eventRemovedTitles: [] }, ); // Flush triggers onWillSaveState; the metadata must survive so the @@ -862,6 +863,8 @@ suite('RemoteAgentHostSessionsProvider', () => { provider.unpublishCachedSessions(); assert.strictEqual(provider.getSessions().length, 0); + const events: ISessionChangeEvent[] = []; + disposables.add(provider.onDidChangeSessions(e => events.push(e))); // Simulate the host coming back online with a fresh connection that // still reports the same session. @@ -872,8 +875,16 @@ suite('RemoteAgentHostSessionsProvider', () => { await timeout(0); assert.deepStrictEqual( - provider.getSessions().map(s => s.title.get()), - ['Restore Me'], + { + sessions: provider.getSessions().map(s => s.title.get()), + added: events.flatMap(e => e.added.map(s => s.title.get())), + removed: events.flatMap(e => e.removed.map(s => s.title.get())), + }, + { + sessions: ['Restore Me'], + added: ['Restore Me'], + removed: [], + }, ); })); From d1610046f61a99d7291a39112112e56941f1df06 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 25 May 2026 18:48:24 -0700 Subject: [PATCH 2/2] Address remote session reconnect review Report updated cached sessions as changed when re-announcing them after a remote reconnect, and fix the sessions list documentation grammar. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/vs/sessions/SESSIONS_LIST.md | 2 +- .../agentHost/browser/baseAgentHostSessionsProvider.ts | 4 +--- .../browser/remoteAgentHostSessionsProvider.test.ts | 10 ++++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/vs/sessions/SESSIONS_LIST.md b/src/vs/sessions/SESSIONS_LIST.md index 09d5f7c92eaf8..c830170a0ebe7 100644 --- a/src/vs/sessions/SESSIONS_LIST.md +++ b/src/vs/sessions/SESSIONS_LIST.md @@ -90,7 +90,7 @@ Pinned sessions appear in a dedicated "Pinned" section at the top. Pin state is - Sessions start as **unread** - A session becomes **read** when the user opens it or explicitly marks it - A session becomes **unread** when it completes in the background (transitions from InProgress to a terminal status while not active) -- Pin and read state is cleaned up when a provider reports a real session removal; remote agent host disconnects hide cached sessions without reporting them as removed +- Pin and read state are cleaned up when a provider reports a real session removal; remote agent host disconnects hide cached sessions without reporting them as removed ### Navigation diff --git a/src/vs/sessions/contrib/providers/agentHost/browser/baseAgentHostSessionsProvider.ts b/src/vs/sessions/contrib/providers/agentHost/browser/baseAgentHostSessionsProvider.ts index c55e4ee01f103..8e2320b65bf1d 100644 --- a/src/vs/sessions/contrib/providers/agentHost/browser/baseAgentHostSessionsProvider.ts +++ b/src/vs/sessions/contrib/providers/agentHost/browser/baseAgentHostSessionsProvider.ts @@ -2061,9 +2061,7 @@ export abstract class BaseAgentHostSessionsProvider extends Disposable implement added.push(existing); } if (existing.update(meta)) { - if (!announceExistingAsAdded) { - changed.push(existing); - } + changed.push(existing); } } else { const cached = this.createAdapter(meta); diff --git a/src/vs/sessions/contrib/providers/remoteAgentHost/test/browser/remoteAgentHostSessionsProvider.test.ts b/src/vs/sessions/contrib/providers/remoteAgentHost/test/browser/remoteAgentHostSessionsProvider.test.ts index 6d2f87c9619af..c63a268b00beb 100644 --- a/src/vs/sessions/contrib/providers/remoteAgentHost/test/browser/remoteAgentHostSessionsProvider.test.ts +++ b/src/vs/sessions/contrib/providers/remoteAgentHost/test/browser/remoteAgentHostSessionsProvider.test.ts @@ -867,10 +867,10 @@ suite('RemoteAgentHostSessionsProvider', () => { disposables.add(provider.onDidChangeSessions(e => events.push(e))); // Simulate the host coming back online with a fresh connection that - // still reports the same session. + // still reports the same session with updated metadata. const reconnected = new MockAgentConnection(); disposables.add(toDisposable(() => reconnected.dispose())); - reconnected.addSession(createSession('restore-me', { summary: 'Restore Me' })); + reconnected.addSession(createSession('restore-me', { summary: 'Restored' })); provider.setConnection(reconnected); await timeout(0); @@ -878,11 +878,13 @@ suite('RemoteAgentHostSessionsProvider', () => { { sessions: provider.getSessions().map(s => s.title.get()), added: events.flatMap(e => e.added.map(s => s.title.get())), + changed: events.flatMap(e => e.changed.map(s => s.title.get())), removed: events.flatMap(e => e.removed.map(s => s.title.get())), }, { - sessions: ['Restore Me'], - added: ['Restore Me'], + sessions: ['Restored'], + added: ['Restored'], + changed: ['Restored'], removed: [], }, );