sessions: badge the activity bar Changes entry on unseen changes and show its stats on hover #343335

Merged
sandy081 opened 3:41pm on October 21, 2026 wanted to merge 737 Δ into microsoft/vscode main from
sandy081/agents/blue-badge-new-changes-hover-stats

Pull Request Overview

  • Opened on October 21, 2026
  • Status Merged
  • Commit count 3 with first commit October 21, 2026

Total Delta

737 Total Diff Delta

Open Days

Open 9 weekdays

Test Delta

509 Diff Delta in Test Files
Breakdown by Phase

How long has this pull request spent in each phase of its lifecycle?

Fraction of total time Business days Phase
 
0.0 days Authoring 2 commits before pull request opened for review
 
0.0 days Awaiting first review
 
9.0 days Merge to deploy (still awaiting deployment)

Total time for pull request 0.0 business days from first commit authored to merge
Still awaiting deployment

Author avatar

sessions: badge the activity bar Changes entry on unseen changes and show its stats on hover

What

In the Agents window, the Changes entry of the Session Activity Bar now:


  • shows a blue badge (dot, activityBarBadge colors) over the icon's bottom-right corner — like the workbench activity bar — when the active chat has changes the user has not yet seen in the Changes tab;

  • shows the same stats as the Changes pill (<n> Files +ins -del) in its hover and accessible label.

How



  • Activity service. New ISessionActivityBarActivityService (services/activity) mirrors the workbench IActivityService: showActivity(commandId, IActivity) / getActivity / onDidChangeActivity, reusing the workbench IActivity/IBadge types. Every Session Activity Bar entry is now a SessionActivityBarActionViewItem that renders reported badges (count for NumberBadge, dot otherwise) and appends badge descriptions to its hover/aria label.


  • Detecting new changes (contrib/changes/browser/changesActivity.ts). Per chat, we remember the Changes pill stats last seen while the session's Changes tab was shown (side pane visible, or in an auxiliary window). The badge shows when the current stats differ and there are changed files. The first observation of a chat only seeds the baseline, and stats are ignored while not loaded yet (changeset loading, or only a summary is known — Copilot provider restores), so opening/restoring sessions does not badge. Seen state is persisted (application storage, LRU 200 chats).


  • Hover. The Changes entry uses a custom view item (via IActionViewItemService) that extends the core one and renders the pill stats in the pill's colors.

  • Accessibility help for the Session Activity Bar mentions the new label content.

Notes for reviewers


  • Known limitation: edits that leave the exact file/line counts unchanged are not detected; reverting changes while not looking badges (stats differ from what was seen).

  • New fixture variant sessionActivityBar/NewChanges (Dark/Light/HC) — screenshot baselines need to be accepted from CI.

Screenshots

Badge on unseen changes (live)
Hover shows the pill stats (live)

Changes entry with a blue badge over the icon's bottom-right corner
Hover on the Changes entry: Changes, then 1 File +4 -0

Badge across themes (fixture sessionActivityBar/NewChanges: Dark, Light, Dark HC, Light HC):

Session Activity Bar with the Changes badge in Dark, Light, Dark High Contrast and Light High Contrast themes

Testing


  • Unit tests: activity service rendering/placement, seen-store persistence/eviction, contribution behavior (badge on unseen changes, cleared when the Changes tab is shown in the side pane or an auxiliary window, no badge for summary-only restores), loaded-stats helpers. ./scripts/test.sh on the affected suites — 435 passing.


  • npm run typecheck-client, npm run hygiene, ESLint, stylelint, npm run valid-layers-check, npm run check-cyclic-dependencies — clean.

  • Live in a dev build: a real agent turn with the Files tab active badges the entry; activating Changes clears it; reloading and visiting existing sessions (fresh seen state, side panel hidden) shows no false badges.

Comments Threads Pending Resolution

ljharb reviewed on October 21, 2026
ljharb left a comment

> [!WARNING]
> Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a `copilot-code-review.yml` file specifying one with the `runs-on` attribute. See the [docs](https://gh.io/AA11tgch) for more details.

## Copilot review overview

7 open issues

- Medium severity [The rendered badge state is derived by summing all `NumberBadge`s and using `badges[0]` for colors,…](https://github.com/microsoft/vscode/pull/343335#discussion_r4341908856) · New
- Medium severity [The rendered badge state is derived by summing all `NumberBadge`s and using `badges[0]` for colors,…](https://github.com/microsoft/vscode/pull/343335#discussion_r4341908923) · New
- Medium severity [Every `set()` persists the full LRU map to storage via `JSON.stringify(...)`. When stats change…](https://github.com/microsoft/vscode/pull/343335#discussion_r4341908990) · New
- Medium severity [Every `set()` persists the full LRU map to storage via `JSON.stringify(...)`. When stats change…](https://github.com/microsoft/vscode/pull/343335#discussion_r4341909038) · New
- Medium severity [This test asserts exact `getBoundingClientRect()` pixel values for badge placement. Those values…](https://github.com/microsoft/vscode/pull/343335#discussion_r4341909114) · New
-

src/vs/sessions/browser/parts/sessionActivityBar.ts
412
	}
Unspecified Git Hub Copilot Model
413
Unspecified Git Hub Copilot Model
414
	protected getBadges(): readonly IBadge[] {
Unspecified Git Hub Copilot Model
415
		return this.activityService.getActivity(this._menuItemAction.id).map(activity => activity.badge);
Unspecified Git Hub Copilot Model
416
	}
Unspecified Git Hub Copilot Model
src/vs/sessions/contrib/changes/browser/changesActivity.ts
269
				return element;
Unspecified Git Hub Copilot Model
270
			},
Unspecified Git Hub Copilot Model
271
		};
Unspecified Git Hub Copilot Model
272
	}
Unspecified Git Hub Copilot Model
273
}
Unspecified Git Hub Copilot Model
src/vs/sessions/browser/parts/sessionActivityBar.ts
437
		badgeElement.classList.toggle('visible', count > 0 || badges.some(badge => !(badge instanceof NumberBadge)));
Unspecified Git Hub Copilot Model
438
		badgeElement.classList.toggle('count', count > 0);
Unspecified Git Hub Copilot Model
439
		badgeElement.textContent = count > 99 ? '99+' : count > 0 ? `${count}` : '';
Unspecified Git Hub Copilot Model
440
Unspecified Git Hub Copilot Model
441
		const colors = badges[0]?.getColors(this._themeService.getColorTheme());
Unspecified Git Hub Copilot Model
src/vs/sessions/browser/parts/sessionActivityBar.ts
422
			if (description) {
Unspecified Git Hub Copilot Model
423
				title = localize('sessionActivityBar.titleWithActivity', "{0} - {1}", title, description);
Unspecified Git Hub Copilot Model
424
			}
Unspecified Git Hub Copilot Model
425
		}
Unspecified Git Hub Copilot Model
426
		return title;
Unspecified Git Hub Copilot Model
src/vs/sessions/test/browser/sessionActivityBar.test.ts
230
			withDot: [{ ariaLabel: 'Changes - New changes', badge: 'dot' }, { ariaLabel: 'Browser', badge: undefined }],
231
			dotPosition: { top: 12, right: 2, width: 8, height: 8 },
232
			withCount: [{ ariaLabel: 'Changes - New changes', badge: 'dot' }, { ariaLabel: 'Browser - 120 pages', badge: '99+' }],
233
			cleared: [{ ariaLabel: 'Changes', badge: undefined }, { ariaLabel: 'Browser', badge: undefined }],
234
		});
src/vs/sessions/test/browser/sessionActivityBar.test.ts
217
		}
218
		const changesItem = activityBar.element.querySelector<HTMLElement>('.action-item')!;
219
		const itemBounds = changesItem.getBoundingClientRect();
220
		const badgeBounds = changesItem.querySelector('.session-activity-bar-badge')!.getBoundingClientRect();
221
		const dotPosition = { top: badgeBounds.top - itemBounds.top, right: itemBounds.right - badgeBounds.right, width: badgeBounds.width, height: badgeBounds.height };

Resolved Comment Threads

ljharb reviewed on October 21, 2026
ljharb left a comment

> [!WARNING]
> Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a `copilot-code-review.yml` file specifying one with the `runs-on` attribute. See the [docs](https://gh.io/AA11tgch) for more details.

## Copilot review overview

7 open issues

- Medium severity [The rendered badge state is derived by summing all `NumberBadge`s and using `badges[0]` for colors,…](https://github.com/microsoft/vscode/pull/343335#discussion_r4341908856) · New
- Medium severity [The rendered badge state is derived by summing all `NumberBadge`s and using `badges[0]` for colors,…](https://github.com/microsoft/vscode/pull/343335#discussion_r4341908923) · New
- Medium severity [Every `set()` persists the full LRU map to storage via `JSON.stringify(...)`. When stats change…](https://github.com/microsoft/vscode/pull/343335#discussion_r4341908990) · New
- Medium severity [Every `set()` persists the full LRU map to storage via `JSON.stringify(...)`. When stats change…](https://github.com/microsoft/vscode/pull/343335#discussion_r4341909038) · New
- Medium severity [This test asserts exact `getBoundingClientRect()` pixel values for badge placement. Those values…](https://github.com/microsoft/vscode/pull/343335#discussion_r4341909114) · New
-

src/vs/sessions/contrib/changes/browser/changesActivity.ts
Outdated
100
101
		this.entries.set(updated, undefined);
102
		const stored = [...
updated
].map(([chat, stats]) => ({ chat, ...stats } satisfies IStoredSeenChanges));
Unspecified Git Hub Copilot Model
103
		this.storageService.store(SEEN_CHANGES_STORAGE_KEY, JSON.stringify(stored), StorageScope.APPLICATION, StorageTarget.MACHINE);
104
	}