From 9ddc847ccfa2f30221ab4b46b4e8f307e3bd6669 Mon Sep 17 00:00:00 2001 From: Osvaldo Ortega Date: Thu, 19 Mar 2026 12:49:13 -0600 Subject: [PATCH 1/7] Add recent repository label tracking to agent sessions control --- .../agentSessions/agentSessionsControl.ts | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsControl.ts b/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsControl.ts index 1130ad7925e..f8d7b8f2448 100644 --- a/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsControl.ts +++ b/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsControl.ts @@ -13,7 +13,7 @@ import { StandardKeyboardEvent } from '../../../../../base/browser/keyboardEvent import { KeyCode } from '../../../../../base/common/keyCodes.js'; import { localize } from '../../../../../nls.js'; import { AgentSessionSection, IAgentSession, IAgentSessionSection, IAgentSessionsModel, IMarshalledAgentSessionContext, isAgentSession, isAgentSessionSection } from './agentSessionsModel.js'; -import { AgentSessionListItem, AgentSessionRenderer, AgentSessionsAccessibilityProvider, AgentSessionsCompressionDelegate, AgentSessionsDataSource, AgentSessionsDragAndDrop, AgentSessionsIdentityProvider, AgentSessionsKeyboardNavigationLabelProvider, AgentSessionsListDelegate, AgentSessionSectionRenderer, AgentSessionsSorter, IAgentSessionsFilter } from './agentSessionsViewer.js'; +import { AgentSessionListItem, AgentSessionRenderer, AgentSessionsAccessibilityProvider, AgentSessionsCompressionDelegate, AgentSessionsDataSource, AgentSessionsDragAndDrop, AgentSessionsIdentityProvider, AgentSessionsKeyboardNavigationLabelProvider, AgentSessionsListDelegate, AgentSessionSectionRenderer, AgentSessionsSorter, getRepositoryName, IAgentSessionsFilter } from './agentSessionsViewer.js'; import { AgentSessionsGrouping } from './agentSessionsFilter.js'; import { AgentSessionApprovalModel } from './agentSessionApprovalModel.js'; import { FuzzyScore } from '../../../../../base/common/filters.js'; @@ -80,8 +80,11 @@ export class AgentSessionsControl extends Disposable implements IAgentSessionsCo private emptyFilterMessage: HTMLElement | undefined; private sessionsList: WorkbenchCompressibleAsyncDataTree | undefined; + private static readonly RECENT_SESSIONS_FOR_EXPAND = 5; + private sessionsListFindIsOpen = false; private _isProgrammaticCollapseChange = false; + private readonly _recentRepositoryLabels = new Set(); private readonly updateSessionsListThrottler = this._register(new Throttler()); @@ -238,6 +241,9 @@ export class AgentSessionsControl extends Disposable implements IAgentSessionsCo if (element.section === AgentSessionSection.Yesterday && this.hasTodaySessions()) { return true; // Also collapse Yesterday when there are sessions from Today } + if (element.section === AgentSessionSection.Repository && !this._recentRepositoryLabels.has(element.label)) { + return true; // Collapse repository sections that don't contain recent sessions + } } } @@ -305,6 +311,7 @@ export class AgentSessionsControl extends Disposable implements IAgentSessionsCo } })); + this.computeRecentRepositoryLabels(); list.setInput(model); this._register(list.onDidOpen(e => this.openAgentSession(e))); @@ -376,6 +383,22 @@ export class AgentSessionsControl extends Disposable implements IAgentSessionsCo ); } + private computeRecentRepositoryLabels(): void { + this._recentRepositoryLabels.clear(); + + const sessions = this.agentSessionsService.model.sessions + .filter(s => !s.isArchived() && !s.isPinned()) + .sort((a, b) => b.timing.created - a.timing.created) + .slice(0, AgentSessionsControl.RECENT_SESSIONS_FOR_EXPAND); + + for (const session of sessions) { + const name = getRepositoryName(session); + if (name) { + this._recentRepositoryLabels.add(name); + } + } + } + private async openAgentSession(e: IOpenEvent): Promise { const element = e.element; if (!element || isAgentSessionSection(element)) { @@ -511,6 +534,7 @@ export class AgentSessionsControl extends Disposable implements IAgentSessionsCo async update(): Promise { return this.updateSessionsListThrottler.queue(async () => { + this.computeRecentRepositoryLabels(); await this.sessionsList?.updateChildren(); this._onDidUpdate.fire(); From f533b7f36552886684b6b4b401408f7009f1ccd9 Mon Sep 17 00:00:00 2001 From: Osvaldo Ortega Date: Thu, 19 Mar 2026 13:24:30 -0600 Subject: [PATCH 2/7] Add command to sessionStart hook for environment setup --- .github/hooks/hooks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/hooks/hooks.json b/.github/hooks/hooks.json index 4457634963e..3d50f6f21ec 100644 --- a/.github/hooks/hooks.json +++ b/.github/hooks/hooks.json @@ -4,7 +4,7 @@ "sessionStart": [ { "type": "command", - "bash": "" + "bash": "nvm use && npm i" } ], "sessionEnd": [ From 6e247436e9eeeb3fdf51bf5839ab9a693ca8b915 Mon Sep 17 00:00:00 2001 From: Osvaldo Ortega Date: Thu, 19 Mar 2026 13:32:59 -0600 Subject: [PATCH 3/7] Add resetSectionCollapseState method to AgentSessionsControl and invoke it in AgenticSessionsViewPane --- src/vs/sessions/contrib/sessions/browser/sessionsViewPane.ts | 1 + .../contrib/chat/browser/agentSessions/agentSessions.ts | 2 ++ .../chat/browser/agentSessions/agentSessionsControl.ts | 4 ++++ 3 files changed, 7 insertions(+) diff --git a/src/vs/sessions/contrib/sessions/browser/sessionsViewPane.ts b/src/vs/sessions/contrib/sessions/browser/sessionsViewPane.ts index 86c6a2cc0ba..5400ea7055c 100644 --- a/src/vs/sessions/contrib/sessions/browser/sessionsViewPane.ts +++ b/src/vs/sessions/contrib/sessions/browser/sessionsViewPane.ts @@ -238,6 +238,7 @@ export class AgenticSessionsViewPane extends ViewPane { this.storageService.store(GROUPING_STORAGE_KEY, this.currentGrouping, StorageScope.PROFILE, StorageTarget.USER); this.isGroupedByRepoKey?.set(this.currentGrouping === AgentSessionsGrouping.Repository); + this.sessionsControl?.resetSectionCollapseState(); this.sessionsControl?.update(); } } diff --git a/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessions.ts b/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessions.ts index f01ebe74835..d5df09ff377 100644 --- a/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessions.ts +++ b/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessions.ts @@ -171,6 +171,8 @@ export interface IAgentSessionsControl { clearFocus(): void; hasFocusOrSelection(): boolean; + + resetSectionCollapseState(): void; } export const agentSessionReadIndicatorForeground = registerColor( diff --git a/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsControl.ts b/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsControl.ts index f8d7b8f2448..5865e383517 100644 --- a/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsControl.ts +++ b/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsControl.ts @@ -218,6 +218,10 @@ export class AgentSessionsControl extends Disposable implements IAgentSessionsCo this.storageService.store(AgentSessionsControl.SECTION_COLLAPSE_STATE_KEY, JSON.stringify(state), StorageScope.PROFILE, StorageTarget.USER); } + resetSectionCollapseState(): void { + this.storageService.remove(AgentSessionsControl.SECTION_COLLAPSE_STATE_KEY, StorageScope.PROFILE); + } + private createList(container: HTMLElement): void { const collapseByDefault = (element: unknown) => { if (isAgentSessionSection(element)) { From 6a8fe54a3d535770255a7208d8c404999564f484 Mon Sep 17 00:00:00 2001 From: Osvaldo Ortega Date: Thu, 19 Mar 2026 13:37:42 -0600 Subject: [PATCH 4/7] Call resetSectionCollapseState on sessionsControl after updating grouping state --- src/vs/sessions/contrib/sessions/browser/sessionsViewPane.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/sessions/contrib/sessions/browser/sessionsViewPane.ts b/src/vs/sessions/contrib/sessions/browser/sessionsViewPane.ts index 5400ea7055c..e171f09b5ed 100644 --- a/src/vs/sessions/contrib/sessions/browser/sessionsViewPane.ts +++ b/src/vs/sessions/contrib/sessions/browser/sessionsViewPane.ts @@ -238,6 +238,7 @@ export class AgenticSessionsViewPane extends ViewPane { this.storageService.store(GROUPING_STORAGE_KEY, this.currentGrouping, StorageScope.PROFILE, StorageTarget.USER); this.isGroupedByRepoKey?.set(this.currentGrouping === AgentSessionsGrouping.Repository); + // TODO: Unsure if this is going to be annoying or helpful so that you can quickly see the active sessions this.sessionsControl?.resetSectionCollapseState(); this.sessionsControl?.update(); } From 9b8ad503fd97c53051866e0b93e198c1eb752e6f Mon Sep 17 00:00:00 2001 From: Osvaldo Ortega Date: Thu, 19 Mar 2026 13:40:41 -0600 Subject: [PATCH 5/7] Clean up --- .github/hooks/hooks.json | 2 +- src/vs/sessions/contrib/sessions/browser/sessionsViewPane.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/hooks/hooks.json b/.github/hooks/hooks.json index 3d50f6f21ec..4457634963e 100644 --- a/.github/hooks/hooks.json +++ b/.github/hooks/hooks.json @@ -4,7 +4,7 @@ "sessionStart": [ { "type": "command", - "bash": "nvm use && npm i" + "bash": "" } ], "sessionEnd": [ diff --git a/src/vs/sessions/contrib/sessions/browser/sessionsViewPane.ts b/src/vs/sessions/contrib/sessions/browser/sessionsViewPane.ts index e171f09b5ed..d506bb6062c 100644 --- a/src/vs/sessions/contrib/sessions/browser/sessionsViewPane.ts +++ b/src/vs/sessions/contrib/sessions/browser/sessionsViewPane.ts @@ -238,7 +238,7 @@ export class AgenticSessionsViewPane extends ViewPane { this.storageService.store(GROUPING_STORAGE_KEY, this.currentGrouping, StorageScope.PROFILE, StorageTarget.USER); this.isGroupedByRepoKey?.set(this.currentGrouping === AgentSessionsGrouping.Repository); - // TODO: Unsure if this is going to be annoying or helpful so that you can quickly see the active sessions + // TODO @osortega: Unsure if this is going to be annoying or helpful so that you can quickly see the active sessions this.sessionsControl?.resetSectionCollapseState(); this.sessionsControl?.update(); } From 3cf5a7edcdde0143bed6d5cb58e1db063ea1dd65 Mon Sep 17 00:00:00 2001 From: Osvaldo Ortega Date: Thu, 19 Mar 2026 14:01:38 -0600 Subject: [PATCH 6/7] Include "Other" repo label in recent repository tracking Sessions without a detected repository are grouped under "Other" in repository grouping. Previously, computeRecentRepositoryLabels skipped these sessions, so the "Other" section would always default to collapsed even if it contained recent sessions. Now we add the unknown repository label to the set when getRepositoryName returns undefined. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../chat/browser/agentSessions/agentSessionsControl.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsControl.ts b/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsControl.ts index 5865e383517..ede2cc48171 100644 --- a/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsControl.ts +++ b/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsControl.ts @@ -81,6 +81,7 @@ export class AgentSessionsControl extends Disposable implements IAgentSessionsCo private sessionsList: WorkbenchCompressibleAsyncDataTree | undefined; private static readonly RECENT_SESSIONS_FOR_EXPAND = 5; + private static readonly UNKNOWN_REPOSITORY_LABEL = localize('agentSessions.noRepository', "Other"); private sessionsListFindIsOpen = false; private _isProgrammaticCollapseChange = false; @@ -397,9 +398,7 @@ export class AgentSessionsControl extends Disposable implements IAgentSessionsCo for (const session of sessions) { const name = getRepositoryName(session); - if (name) { - this._recentRepositoryLabels.add(name); - } + this._recentRepositoryLabels.add(name ?? AgentSessionsControl.UNKNOWN_REPOSITORY_LABEL); } } From 58c1dd95001a4760a26969e24d8b3c627a2394b0 Mon Sep 17 00:00:00 2001 From: Osvaldo Ortega Date: Thu, 19 Mar 2026 14:06:14 -0600 Subject: [PATCH 7/7] Centralize "Other" label and ensure ordering in repo grouping Move the localized "Other" label into AgentSessionSectionLabels so both groupSessionsByRepository and computeRecentRepositoryLabels share the same string. Ensure the "Other" group always appears after all named repository sections and just above Archived. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../agentSessions/agentSessionsControl.ts | 5 ++--- .../agentSessions/agentSessionsViewer.ts | 17 +++++++++++++++-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsControl.ts b/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsControl.ts index ede2cc48171..7b68c36611a 100644 --- a/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsControl.ts +++ b/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsControl.ts @@ -13,7 +13,7 @@ import { StandardKeyboardEvent } from '../../../../../base/browser/keyboardEvent import { KeyCode } from '../../../../../base/common/keyCodes.js'; import { localize } from '../../../../../nls.js'; import { AgentSessionSection, IAgentSession, IAgentSessionSection, IAgentSessionsModel, IMarshalledAgentSessionContext, isAgentSession, isAgentSessionSection } from './agentSessionsModel.js'; -import { AgentSessionListItem, AgentSessionRenderer, AgentSessionsAccessibilityProvider, AgentSessionsCompressionDelegate, AgentSessionsDataSource, AgentSessionsDragAndDrop, AgentSessionsIdentityProvider, AgentSessionsKeyboardNavigationLabelProvider, AgentSessionsListDelegate, AgentSessionSectionRenderer, AgentSessionsSorter, getRepositoryName, IAgentSessionsFilter } from './agentSessionsViewer.js'; +import { AgentSessionListItem, AgentSessionRenderer, AgentSessionsAccessibilityProvider, AgentSessionsCompressionDelegate, AgentSessionsDataSource, AgentSessionsDragAndDrop, AgentSessionsIdentityProvider, AgentSessionsKeyboardNavigationLabelProvider, AgentSessionsListDelegate, AgentSessionSectionRenderer, AgentSessionSectionLabels, AgentSessionsSorter, getRepositoryName, IAgentSessionsFilter } from './agentSessionsViewer.js'; import { AgentSessionsGrouping } from './agentSessionsFilter.js'; import { AgentSessionApprovalModel } from './agentSessionApprovalModel.js'; import { FuzzyScore } from '../../../../../base/common/filters.js'; @@ -81,7 +81,6 @@ export class AgentSessionsControl extends Disposable implements IAgentSessionsCo private sessionsList: WorkbenchCompressibleAsyncDataTree | undefined; private static readonly RECENT_SESSIONS_FOR_EXPAND = 5; - private static readonly UNKNOWN_REPOSITORY_LABEL = localize('agentSessions.noRepository', "Other"); private sessionsListFindIsOpen = false; private _isProgrammaticCollapseChange = false; @@ -398,7 +397,7 @@ export class AgentSessionsControl extends Disposable implements IAgentSessionsCo for (const session of sessions) { const name = getRepositoryName(session); - this._recentRepositoryLabels.add(name ?? AgentSessionsControl.UNKNOWN_REPOSITORY_LABEL); + this._recentRepositoryLabels.add(name ?? AgentSessionSectionLabels[AgentSessionSection.Repository]); } } diff --git a/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsViewer.ts b/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsViewer.ts index 6eed12abc64..503ef3668e6 100644 --- a/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsViewer.ts +++ b/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsViewer.ts @@ -899,7 +899,7 @@ export class AgentSessionsDataSource extends Disposable implements IAsyncDataSou const pinnedSessions: IAgentSession[] = []; const archivedSessions: IAgentSession[] = []; const unknownKey = '\x00unknown'; - const unknownLabel = localize('agentSessions.noRepository', "Other"); + const unknownLabel = AgentSessionSectionLabels[AgentSessionSection.Repository]; for (const session of sortedSessions) { if (session.isArchived()) { @@ -937,7 +937,10 @@ export class AgentSessionsDataSource extends Disposable implements IAsyncDataSou }); } - for (const [, { label, sessions }] of repoMap) { + for (const [repoId, { label, sessions }] of repoMap) { + if (repoId === unknownKey) { + continue; // "Other" group is added after all named repos + } result.push({ section: AgentSessionSection.Repository, label, @@ -945,6 +948,15 @@ export class AgentSessionsDataSource extends Disposable implements IAsyncDataSou }); } + const unknownGroup = repoMap.get(unknownKey); + if (unknownGroup) { + result.push({ + section: AgentSessionSection.Repository, + label: unknownGroup.label, + sessions: unknownGroup.sessions, + }); + } + if (archivedSessions.length > 0) { result.push({ section: AgentSessionSection.Archived, @@ -1112,6 +1124,7 @@ export const AgentSessionSectionLabels = { [AgentSessionSection.Older]: localize('agentSessions.olderSection', "Older"), [AgentSessionSection.Archived]: localize('agentSessions.archivedSection', "Archived"), [AgentSessionSection.More]: localize('agentSessions.moreSection', "More"), + [AgentSessionSection.Repository]: localize('agentSessions.noRepository', "Other"), }; const DAY_THRESHOLD = 24 * 60 * 60 * 1000;