Skip to content

Suggest agent session storage cleanup when worktrees accumulate - #338502

Merged
Megan Rogge (meganrogge) merged 13 commits into
mainfrom
agents/auto-archive-worktrees-notification
Sep 29, 2026
Merged

Megan Rogge (meganrogge) merged 13 commits into
mainfrom
agents/auto-archive-worktrees-notification

Conversation

@meganrogge

@meganrogge Megan Rogge (meganrogge) commented Sep 28, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #338039

Why

Before auto-archive-on-PR-merge settings existed, sessions accumulated and their worktrees now consume significant disk space. Users have no visibility into this and no guided way to clean it up.

What

Suggestion notice — appears below the Sessions list when cleanup is worthwhile:

  • 20+ eligible worktrees exist, or
  • eligible inactive worktrees can reclaim 5 GiB or more

Only eligible candidates count toward the worktree threshold, so the notice is always actionable.

Manage Agent Session Storage editor — review and clean up eligible sessions, with an inactivity-days filter (7/15/30/60/90), a worktree-only filter, and a toggle for the suggestion itself. Only fully eligible sessions are listed.

Dismissal and reset

Action Effect
X or Escape Suppressed for the current window only
Don't Show Again Sets the setting to false at application scope
Opening the manager Suppressed for the current window

Measurements are cached for one hour; changing the days filter recomputes from cache without re-measuring disk.

Settings

chat.agentSessions.sessionStorageCleanupSuggestion.enabled gates the notice.

Accessibility

  • The notice is a labeled region, fully keyboard navigable, and announced once to screen readers.
  • Disable Session Storage Cleanup Suggestions command gives screen reader users a non-visual equivalent of Don't Show Again.
  • The manager is reachable from the Command Palette and the session context menu.
  • The flow is documented in the Sessions accessibility help dialog.

Validation

  • npm run typecheck-client — clean
  • npm run valid-layers-check — clean
  • 45 focused tests passing across the service, configuration, notice, banners, and accessibility help

Detect accumulated session worktrees, surface cleanup guidance, and provide a storage manager with safe manual and automatic lifecycle cleanup.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Users who never enabled auto-archive accumulate session worktrees that
consume significant disk space. Surface a suggestion notice below the
Sessions list when cleanup is worthwhile, and add a Manage Agent Session
Storage editor to review and clean up eligible sessions.

- Show the notice when 20+ eligible worktrees exist or eligible inactive
  worktrees can reclaim 5 GiB or more. Only eligible candidates count
  toward the worktree threshold.
- Add the Manage Agent Session Storage editor with an inactivity-days
  filter, a worktree-only filter, and a suggestion toggle.
- Gate the notice behind chat.agentSessions.sessionStorageCleanupSuggestion.enabled
  and support per-window suppression.
- Add a Disable Session Storage Cleanup Suggestions command so screen
  reader users have a non-visual equivalent of Don't Show Again.
- Announce the notice to screen readers and document the flow in the
  Sessions accessibility help dialog.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings September 28, 2026 18:43
@vs-code-engineering

Copy link
Copy Markdown
Contributor

📬 CODENOTIFY

The following users are being notified based on files changed in this PR:

Sandeep Somavarapu (@sandy081)

Matched files:

  • src/vs/sessions/services/sessions/browser/sessionsManagementService.ts
  • src/vs/sessions/services/sessions/common/sessionsManagement.ts
  • src/vs/sessions/services/sessions/common/sessionsProvider.ts

Ladislau Szomoru (@lszomoru)

Matched files:

  • src/vs/sessions/services/sessions/browser/sessionsManagementService.ts
  • src/vs/sessions/services/sessions/common/sessionsManagement.ts
  • src/vs/sessions/services/sessions/common/sessionsProvider.ts

Marking a session that has no worktree as done reclaims no disk space, so
it does not belong in a storage manager. Report only sessions with a
measured worktree and remove the "Sessions with worktrees only" filter,
the hasWorktree field, and the "No worktree" storage cell. The Sessions
list already owns decluttering sessions that hold no storage.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Active-session protection, remote URI handling, eligibility revalidation, and bounded measurement must be corrected before cleanup is safe.

Review effort: Balanced
Findings: 1 High severity · 5 Medium severity · 1 Low severity

Open (7)
What changed in this PR

Adds guided discovery and cleanup of stale Agent Session worktrees through a Sessions-list notice and storage-management editor.

Changes:

  • Measures and caches Agent Host worktree disk usage.
  • Adds cleanup eligibility, configuration migration, commands, and management UI.
  • Adds accessibility messaging, keyboard dismissal, styling, and focused tests.
File Description
src/​vs/​workbench/​contrib/​chat/​browser/​chat.shared.contribution.ts Loads Agent Sessions configuration.
src/​vs/​sessions/​SESSIONS.md Documents worktree measurement ownership.
src/​vs/​sessions/​services/​sessions/​common/​sessionsProvider.ts Adds provider measurement API.
src/​vs/​sessions/​services/​sessions/​common/​sessionsManagement.ts Exposes measurement through management.
src/​vs/​sessions/​services/​sessions/​browser/​sessionsManagementService.ts Routes measurements to providers.
src/​vs/​sessions/​contrib/​sessions/​test/​browser/​sessionStorageCleanupNotice.test.ts Tests notice behavior.
src/​vs/​sessions/​contrib/​sessions/​browser/​views/​sessionsView.ts Adds the Sessions-list notice.
src/​vs/​sessions/​contrib/​sessions/​browser/​views/​sessionStorageCleanupNotice.ts Implements the cleanup notice.
src/​vs/​sessions/​contrib/​sessions/​browser/​media/​sessionsViewPane.css Styles the notice.
src/​vs/​sessions/​contrib/​sessionInputBanners/​test/​browser/​sessionWorktreeCleanupService.test.ts Tests cleanup eligibility and caching.
src/​vs/​sessions/​contrib/​sessionInputBanners/​test/​browser/​sessionStorageCleanupConfiguration.test.ts Tests setting migration.
src/​vs/​sessions/​contrib/​sessionInputBanners/​browser/​sessionWorktreeCleanupService.ts Implements measurement, eligibility, and cleanup.
src/​vs/​sessions/​contrib/​sessionInputBanners/​browser/​sessionWorktreeCleanupEditorInput.ts Defines the manager editor input.
src/​vs/​sessions/​contrib/​sessionInputBanners/​browser/​sessionWorktreeCleanupEditor.ts Implements the management editor.
src/​vs/​sessions/​contrib/​sessionInputBanners/​browser/​sessionStorageCleanupConfiguration.ts Migrates the legacy setting.
src/​vs/​sessions/​contrib/​sessionInputBanners/​browser/​sessionInputBannerWidget.ts Adds Escape dismissal.
src/​vs/​sessions/​contrib/​sessionInputBanners/​browser/​sessionInputBanners.ts Refactors banner-state initialization.
src/​vs/​sessions/​contrib/​sessionInputBanners/​browser/​media/​sessionWorktreeCleanupEditor.css Styles the management editor.
src/​vs/​sessions/​contrib/​providers/​agentHost/​test/​browser/​worktreeDiskUsage.test.ts Tests disk traversal.
src/​vs/​sessions/​contrib/​providers/​agentHost/​browser/​worktreeDiskUsage.ts Measures worktree contents.
src/​vs/​sessions/​contrib/​providers/​agentHost/​browser/​baseAgentHostSessionsProvider.ts Implements Agent Host measurement.
src/​vs/​sessions/​contrib/​chat/​browser/​sessionsChatAccessibilityHelp.ts Documents the cleanup notice.
src/​vs/​sessions/​contrib/​chat/​browser/​chat.contribution.ts Registers settings, service, editor, and commands.

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/vs/sessions/contrib/providers/agentHost/browser/worktreeDiskUsage.ts Outdated
Comment thread src/vs/workbench/contrib/chat/browser/chat.shared.contribution.ts Outdated
The dismiss (x) button used a monaco Button whose background and border
come from inline styles, so the intended borderless look could not be
applied from CSS and it rendered as a bordered box. Render it as a plain
borderless icon button matching the session input banner's close button,
with a hover background and focus-visible outline, and use IHoverService
for its tooltip.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@meganrogge Megan Rogge (meganrogge) added this to the 1.141.0 milestone Sep 28, 2026
- Identify the active session by sessionId instead of reference equality,
  since the active session is a VisibleSession wrapper while getSessions
  returns provider facades.
- Aggregate disk usage across every worktree an Agent Host session owns
  instead of only the first, and carry the worktree count through the
  cleanup model so the worktree threshold counts worktrees, not sessions.
- Only suppress the protocol NotFound error when measuring worktree disk
  usage; auth, permission, and transport failures now surface.
- Collapse the storage editor table to a stacked layout in narrow windows
  so Storage and Open Session are not clipped.
- Add an editor-scoped accessibility help provider, focus context key, and
  verbosity setting, and exclude the storage editor from the window-wide
  chat help.
- Report archiving rejections through the notification service instead of
  leaving an unhandled rejection.
- Remove a duplicate side-effect import of agentSessionsConfiguration.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The setting was tagged experimental but lacked the experiment metadata, so
ExP could not set its default through the config.<setting id> treatment.
Add experiment: { mode: 'auto' } to match the other experimental Agents
window settings.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…orktrees-notification

# Conflicts:
#	src/vs/sessions/contrib/sessions/browser/views/sessionsView.ts
roblourens
roblourens previously approved these changes Sep 28, 2026
- Focus the primary actions on the cleanup outcome: "Clean Up Agent
  Worktrees" for the command, editor, and Sessions-list notice, and
  "Clean Up {N} Worktrees" for the modal's primary button and confirmation.
- Open a session from its title link in the table and drop the separate
  Actions/Open Session column.
- Make the in-modal settings read clearly as settings: a "Cleanup Settings"
  heading with a gear icon and a note that the options change your settings.
- Update the accessibility help and tests to match the new terminology and
  the title-link interaction.

Keeps the settings inline in the modal and the entry point below the
Sessions list per the Agents window discussion.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Drop the redundant "return here any time" paragraph from the editor.
- Move the primary cleanup button up beside the summary line, right
  aligned, instead of a full-width row below it.
- Remove the database icon from the Sessions-list notice to reclaim
  horizontal space and align the text and actions to the edge.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Each row in the Cleanup Settings section now has a gear button that opens
the Settings editor filtered to that setting, for users who prefer to
configure it there directly. The button names the setting for its hover
tooltip and its screen reader label.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
}
try {
const worktrees = await this.cleanupService.getWorktrees(this.minimumAgeDays);
if (version !== this.renderVersion || !this.list || !this.summary) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI Review: Closing the whole modal during measurement disposes the pane, but leaves renderVersion and the DOM references valid. The late render registers widgets and a summary hover with an already-disposed store, losing their disposal handles. HoverService's strong map retains the detached DOM and row callbacks capturing the pane/session. This retention was reproduced after GC in a current-source browser component fixture, not a full-workbench run. Invalidate loads on clearInput/disposal, check cancellation/disposal before rendering, and add a delayed-load disposal test.

const batch = pending.splice(0, MAX_CONCURRENT_REQUESTS);
const entries = await Promise.all(batch.map(async directory => {
const children = await connection.resourceList(directory);
return Promise.all(children.entries.map(async child => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI Review: The limit of eight bounds directories, not the child requests started by this Promise.all. A production-path fixture with 1,024 real files reached 1,024 pending RPCs and outstanding stat promises; a shared Limiter(8) capped them at eight with the same byte total. Concurrent worktrees multiply this burst on the connection serving active sessions. Bound individual filesystem requests across the walks, or aggregate on the host, and add a wide-directory concurrency test.

}

for (const candidate of selected) {
await this.sessionsManagementService.archiveSession(candidate.session);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI Review: Eligibility is a snapshot from the editor load. A selected session can resume through another client while confirmation is open, yet this loop still archives it. SessionsManagementService.archiveSession cancels active chats first, so cleanup stops newly running work despite the promised protection. Pass the selected inactivity cutoff into this operation and recheck each current session immediately before archiving, skipping candidates that became active, running, needs-input, pinned, recent, or otherwise ineligible. Cover a status change during confirmation.

if (worktrees.size === 0) {
return undefined;
}
const sizes = await Promise.all([...worktrees.values()].map(worktreeUri => getWorktreeDiskUsage(connection, worktreeUri)));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI Review: Remote worktree locations are client-side vscode-agent-host: URIs, but these direct connection calls forward them unchanged. The standalone host cannot resolve that scheme and returns NotFound, which the walker treats as a missing root. Existing remote worktrees are consequently omitted from the editor and suggestion totals. Apply the already-imported fromAgentHostUri exactly once before walking each root, and add a wrapped-URI regression test; local and decoded inputs should measure the same directory.


private async _getMeasuredWorktrees(minimumAgeDays: number): Promise<readonly ISessionWorktree[]> {
const measuredSessionSignature = this._getWorktreeSessionSignature();
if (this._lastMeasuredWorktrees && this._lastMeasuredSessionSignature === measuredSessionSignature && Date.now() - this._lastMeasurementAt < SCAN_CACHE_DURATION_MS) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI Review: With suggestions enabled, adding one worktree changes this global signature and remeasures every existing tree after the ten-second debounce, even when the cache is seconds old. Adding one recent session after measuring 20 worktrees caused 21 more measurements. Archive/removal also invalidates unrelated fresh sizes. Reconcile the current catalog while retaining unexpired measurements for unchanged worktree identities, and invalidate only affected entries, so ordinary session creation does not defeat the advertised hourly measurement reuse.

}));
}

const worktrees = await this._measureWorktrees(minimumAgeDays);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI Review: _refreshPromise coalesces automatic refreshes, but not getWorktrees(). Changing the age filter during a cold scan, or opening the editor during background measurement, starts another full traversal with its own limiter. Three overlapping loads over four unchanged sessions produced 12 measurements with six in flight; renderVersion only discarded their UI results. Share the pending size measurement across callers, then recompute each caller's age cutoff and live protection state after awaiting it.

},
}));
const storageCleanupNotice = this._register(this.instantiationService.createInstance(SessionStorageCleanupNotice, () => sessionsControl.focus(), status));
sessionsContent.appendChild(storageCleanupNotice.domNode);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI Review: When measurement makes this notice visible, it shrinks the flex-sized list container without calling sessionsControl.layout. ListView retains the larger logical viewport and clamps scrolling too early, leaving the final rows clipped until a sidebar/window layout. A current-source browser fixture reproduced this, including the inverse mismatch on dismissal. Notify the owning view when the notice changes height, or observe the list container and run its existing layout path.

header.appendChild(selectAll.domNode);
this.rowDisposables.add(selectAll.onChange(() => {
this.selectedSessionIds = selectAll.checked ? new Set(eligible.map(worktree => worktree.session.sessionId)) : new Set();
this.renderRows();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI Review: Toggling Select All with Space or Enter synchronously rebuilds the table and removes the focused checkbox. Focus moves to BODY, so a second press no longer toggles the selection. Keep the header checkbox mounted while updating selections, or restore focus to its replacement, and add a keyboard regression test.

await this.load();
}

focusAutomaticCleanup(): void {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI Review: Ordinary command/notice opening relies on pane.focus(), but this editor inherits the no-op Composite.focus(). Focus stays on the modal group's wrapper outside the tracked container; the new cleanup context remains false and Alt+F1 selects chat help. Only the special automatic-section path focuses a control. Override focus() to focus or restore an appropriate cleanup control and test the normal no-argument opening path.

days.value = String(value > 0 ? value : 15);
text.classList.toggle('disabled', !checkbox.checked);
};
update();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI Review: With modal editors enabled, use Configure in Settings to change an automatic-cleanup policy from 15 days to 0, then close only Settings with Ctrl+W. The pinned cleanup pane returns showing a checked checkbox and enabled 15-day input although the policy is disabled; setInput only reloads worktrees. Subscribe to configuration changes affecting this setting and call update(), as the suggestion toggle already does. Add coverage for returning from Settings.

@meganrogge
Megan Rogge (meganrogge) merged commit a58d877 into main Sep 29, 2026
35 checks passed
@meganrogge
Megan Rogge (meganrogge) deleted the agents/auto-archive-worktrees-notification branch September 29, 2026 02:48
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.

Add manual cleanup command for Agents Window worktrees

5 participants