Skip to content

sessions: support nested custom groups - #343024

Merged
Benjamin Christopher Simmonds (benibenj) merged 6 commits into
mainfrom
agents/sessions-list-nested-custom-groups
Oct 20, 2026
Merged

Benjamin Christopher Simmonds (benibenj) merged 6 commits into
mainfrom
agents/sessions-list-nested-custom-groups

Conversation

@benibenj

@benibenj Benjamin Christopher Simmonds (benibenj) commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

What changed

Nested custom groups

  • Custom groups in the Agents window sessions list can contain other custom groups, with no limit on depth.
  • A group lists its subgroups above its own sessions.
  • Each nesting level steps in by one header icon and its gap. A subgroup's header starts where its parent's label starts, and a group's sessions line up with its own header. Top-level groups look the same as today.
  • A group's context menu offers Create Subgroup in place of Create Group. It adds an empty group inside, expands the parent, and starts renaming the new group.
  • Create Group on sessions that all render in the same group creates the new group inside that group.
  • A session's Add to Group / Move to Group submenu lists every group in tree order and names nested groups by their path, for example "Release work › Hotfixes".
  • Mark All as Done and Mark All as Read on a group include its subgroups' sessions. A collapsed group's status icon and accessible label reflect its subgroups too.
  • Delete Group also deletes the group's subgroups and ungroups their sessions. When that removes more than one group, the list's Undo notice ("3 groups deleted") appears. Undo restores the groups, their order, and the sessions' membership, except for sessions marked done or moved to another group since.
  • The "No session" placeholder and the empty-group trash button only appear on groups that have neither sessions nor subgroups.
  • Comparison groups don't nest and can't contain subgroups.

Drag and drop

  • Dropping a group on the middle of another group's header nests it there as the first subgroup, and that header highlights. The same happens when dropping on the lower edge of an expanded header or on any row inside the group.
  • Dropping on the top or bottom edge of a header places the group before or after that header, among its siblings. This also moves a nested group back to the top level, next to workspace sections.
  • The insertion line starts at the depth where the group lands. That distinguishes "after the last subgroup" from "before the next top-level header", which share the same vertical position.
  • A group can't be dropped into itself or one of its subgroups. Dragging sessions works as before, at any depth.

Design notes for reviewers

  • Model and ownership:
    • ISessionGroup gains an optional parentId.
    • ISessionGroupsService owns the hierarchy, the order of each group's subgroups, and their invariants:
      • createGroup(name, members, parentId). A new subgroup comes first among its siblings.
      • moveGroup(groupId, parentId, placement?) nests a group and places it next to a sibling, or first, in one change and one event. It refuses moves that would create a cycle.
      • deleteGroup deletes the subtree and returns IDeletedSessionGroups, which includes the deleted groups' positions. restoreGroups brings back the groups, their positions, and the sessions' membership.
      • getTree() returns a SessionGroupTree, a cycle-safe read-only view (getSubgroups, getPath, getSubtree, canMove) in the user's order at every level.
    • On load, groups with a missing or looping parent become top-level, so every group descends from a top-level group.
  • Order:
    • The groups service stores each group's subgroup order with the groups. Subgroups the user hasn't moved stay newest first.
    • ISessionSectionOrderService keeps only the list's top-level headers, where groups share one order with workspace sections, so the list still arranges them. The list prunes groups from it once they are nested.
    • The first version of this PR stored every set of siblings in that one flat order. That let moving a group reorder unrelated groups: for example, nesting a top-level group could swap the two remaining top-level groups, because the merge keeps other entries attached to the entry they follow.
    • The section order service gains capturePositions(ids) and restorePositions(positions), so that Undo restores a top-level group's position.
  • Rendering:
    • ISessionGroupItem gains depth and subtreeSessions.
    • isEmpty now also requires the group to have no subgroups.
    • Row renderers get their depth from one list method and set it as a --session-group-depth CSS variable on their row container. One CSS rule turns it into margin-left. Done chats listed in the Done section don't step in.
  • Drag and drop:
    • The controller asks the list where a dragged header lands (getHeaderDrop), and the list applies it (moveHeader). This replaces the top-level-only reorderSection.
    • A drop lands at the topLevel, where the list reorders its headers; as a subgroup, with an ISessionGroupPlacement for moveGroup; or into a group.
    • One list policy decides where a group can nest: comparison groups neither nest nor hold subgroups, and the tree refuses cycles.
  • Delete with Undo: the list runs the new internal command sessionsViewPane.deleteSessionGroupWithUndo, registered in sessionsViewActions.ts like the archive-with-undo command, so the view can show the notice. The command only calls deleteGroup and, on Undo, restoreGroups.
  • Docs:
    • SESSIONS_LIST.md documents the changed ownership, ordering, nesting, drag-and-drop, and persistence contracts.
    • The Agents window accessibility help describes nested groups.

This PR is independent of #342997 (grouping by repository). Both change the header drag-and-drop code in sessionsList.ts, so whichever merges second needs a small rebase.

How to test

  1. In the Agents window, right-click a custom group's header and choose Create Subgroup. A "New Group" editor appears as the group's first subgroup, stepped in one level. Name it.
  2. Create a subgroup inside that subgroup, and add sessions to each level. Add to Group lists the nested groups as "Parent › Child". Subgroups stay above each group's sessions, and each session lines up with its own group's header.
  3. Drag a top-level group onto the middle of another group's header: it moves inside as the first subgroup. Drag a nested group onto the top edge of a workspace section or a top-level group: it moves back to the top level. Dropping a group onto one of its own subgroups is refused. After each move, the other groups, at every level, keep their order.
  4. Turn on sessions.list.showUnreadInCollapsedSections, then collapse a subgroup that contains an unread or waiting session. Its header shows that status.
  5. Right-click a group that has subgroups and choose Delete Group. A "N groups deleted" notice appears at the bottom of the list. Undo brings back the groups, their order, and their sessions.
  6. Reload the window. Nesting, order, and collapse state persist.

Validation

  • npm run eslint -- <15 changed TypeScript files>: passed, with no warnings or errors.
  • npm run stylelint -- src/vs/sessions/contrib/sessions/browser/media/sessionsList.css: passed. The new CSS variables are registered in vscode-known-variables.json. The file's 3 design-token suggestions also appear on main.
  • npm run typecheck-client: no errors in src/vs/sessions. My local checkout reports 134 unrelated errors in agent host Copilot code and an editor test, caused by outdated @github/copilot-sdk and sinon installs.
  • npm run valid-layers-check: no findings in src/vs/sessions. Locally the command fails on 93 errors in src/vs/platform/agentHost/node, caused by the same outdated installs and a missing @hpke/core.
  • npm run transpile-client: passed.
  • node --experimental-strip-types build/hygiene.ts <18 changed files>: passed. The first CI run failed hygiene on the › path separator, which now opts in with // allow-any-unicode-next-line.
  • npm run check-cyclic-dependencies: passed, no cyclic dependencies.
  • Unit tests, run with the Electron test runner (test/unit/electron/index.js, which ./scripts/test.sh launches): --runGlob "**/sessions/**/test/**/*.test.js" gave 7420 passing, 23 pending.
    • New tests cover placing moved groups, a no-op move firing no event, other groups keeping their order, persistence, restoring deleted groups' positions, and done chats in the Done section not stepping in.
    • Without the fix, the done chat test fails.
    • Replaying the first version's flat order through the real reorder helpers swaps the two remaining top-level groups after nesting the third. The new test expects them to keep their order.
  • Component Explorer:
    • I reviewed screenshots of the 11 new non-blocking SessionsList_NestedGroups* fixtures: expanded tree, Time grouping, collapsed subgroup status, Create Subgroup editor, hovered subgroup header, context menu, drag into a group, drag before a subgroup, deep nesting, compact, and phone. After the ordering change, the default, Time grouping, drag before a subgroup, and deep nesting fixtures still match their descriptions.
    • All 194 sessions/sessionsList fixtures render without errors.
    • The drag, menu, and editor fixtures are stable across remounts.
    • Locally, the report-question codicon doesn't render because my outdated codicon font lacks it. This also affects existing fixtures.
  • Not tested: a live Agents window.

Custom groups in the sessions list can now contain other custom groups at any depth. A group lists its subgroups above its own sessions, and each nesting level steps in so that a subgroup's header starts where its parent's label does while sessions line up with their own group's header.

A group's context menu offers Create Subgroup, Create Group on sessions that share a group nests the new group there, and Add to Group names nested groups by their path. Mark All as Done and Mark All as Read on a group include its subgroups' sessions, and a collapsed group's status reflects them too. Deleting a group deletes its subgroups; when that deletes several groups, an Undo notice restores the groups, their order, and their sessions.

Groups can be dragged into another group, or before or after any header among its siblings, never into themselves or their subgroups, and the insertion line starts at the depth where the group lands. The groups service owns the hierarchy and its invariants, and the section order service now orders siblings at every level.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 20, 2026 20:48
@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/sessionGroupsService.ts
  • src/vs/sessions/services/sessions/browser/sessionSectionOrderService.ts
  • src/vs/sessions/services/sessions/test/browser/sessionComparisonService.test.ts
  • src/vs/sessions/services/sessions/test/browser/sessionGroupsService.test.ts
  • src/vs/sessions/services/sessions/test/browser/sessionSectionOrderService.test.ts

Ladislau Szomoru (@lszomoru)

Matched files:

  • src/vs/sessions/services/sessions/browser/sessionGroupsService.ts
  • src/vs/sessions/services/sessions/browser/sessionSectionOrderService.ts
  • src/vs/sessions/services/sessions/test/browser/sessionComparisonService.test.ts
  • src/vs/sessions/services/sessions/test/browser/sessionGroupsService.test.ts
  • src/vs/sessions/services/sessions/test/browser/sessionSectionOrderService.test.ts

@github-actions

github-actions Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Screenshot Changes

Base: ea64e8b6 Current: 2ca2db79

Added (11)

sessions/sessionsList/SessionsList_NestedGroups/Light

current

sessions/sessionsList/SessionsList_NestedGroups_DateGrouping/Light

current

sessions/sessionsList/SessionsList_NestedGroups_CollapsedSubgroup/Light

current

sessions/sessionsList/SessionsList_NestedGroups_CreateSubgroup/Light

current

sessions/sessionsList/SessionsList_NestedGroups_HoveredSubgroupHeader/Light

current

sessions/sessionsList/SessionsList_NestedGroups_ContextMenu/Light

current

sessions/sessionsList/SessionsList_NestedGroups_DragIntoGroup/Light

current

sessions/sessionsList/SessionsList_NestedGroups_DragBeforeSubgroup/Light

current

sessions/sessionsList/SessionsList_NestedGroups_Deep/Light

current

sessions/sessionsList/SessionsList_NestedGroups_Compact/Light

current

sessions/sessionsList/SessionsList_NestedGroups_Phone/Light

current

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.

🟡 Changes recommended

Ordering isolation, standalone-chat alignment, and keyboard access to group movement need correction before approval.

3 open issues
What changed in this PR

Adds nested custom groups to the Agents window sessions list, extending its grouping, ordering, and persistence model.

Changes:

  • Adds group hierarchies, subtree deletion, and Undo restoration.
  • Updates rendering, menus, bulk actions, and drag-and-drop for nested groups.
  • Adds regression tests, visual fixtures, and accessibility documentation.
File Description
src/​vs/​sessions/​SESSIONS_LIST.md Documents nesting and ownership contracts.
src/​vs/​sessions/​services/​sessions/​test/​browser/​sessionSectionOrderService.test.ts Tests order restoration.
src/​vs/​sessions/​services/​sessions/​test/​browser/​sessionGroupsService.test.ts Tests hierarchy, persistence, and restoration.
src/​vs/​sessions/​services/​sessions/​test/​browser/​sessionComparisonService.test.ts Updates deletion mock contract.
src/​vs/​sessions/​services/​sessions/​browser/​sessionSectionOrderService.ts Captures and restores header positions.
src/​vs/​sessions/​services/​sessions/​browser/​sessionGroupsService.ts Implements hierarchy and subtree operations.
src/​vs/​sessions/​contrib/​sessions/​test/​browser/​sessionsListTestUtils.ts Records group creation and movement.
src/​vs/​sessions/​contrib/​sessions/​test/​browser/​sessionsListFixtureUtils.ts Supports nested fixtures and drag simulation.
src/​vs/​sessions/​contrib/​sessions/​test/​browser/​sessionsListContextMenu.test.ts Tests subgroup menus and path labels.
src/​vs/​sessions/​contrib/​sessions/​test/​browser/​sessionsList.test.ts Tests nested rendering and drops.
src/​vs/​sessions/​contrib/​sessions/​test/​browser/​sessionsList.fixture.ts Adds nested-group visual scenarios.
src/​vs/​sessions/​contrib/​sessions/​test/​browser/​sessionsArchiveActions.test.ts Tests subtree bulk actions and deletion Undo.
src/​vs/​sessions/​contrib/​sessions/​browser/​views/​sessionsViewActions.ts Adds deletion Undo and subtree actions.
src/​vs/​sessions/​contrib/​sessions/​browser/​views/​sessionsList.ts Integrates nested groups into list interactions.
src/​vs/​sessions/​contrib/​sessions/​browser/​media/​sessionsList.css Adds nesting and insertion-line indentation.
src/​vs/​sessions/​contrib/​chat/​browser/​sessionsChatAccessibilityHelp.ts Explains nested-group operations.
src/​vs/​sessions/​common/​sessionCommands.ts Defines the deletion-with-Undo command.
build/​lib/​stylelint/​vscode-known-variables.json Registers nesting CSS variables.

Review effort: Balanced

Comment thread src/vs/sessions/contrib/sessions/browser/views/sessionsList.ts Outdated
Comment thread src/vs/sessions/contrib/sessions/browser/views/sessionsList.ts Outdated
Comment thread src/vs/sessions/contrib/sessions/browser/views/sessionsList.ts Outdated
The groups service now stores the order of each group's subgroups with the
groups, and moveGroup(groupId, parentId, placement) nests and places a group
in one change. The section order keeps only the list's top-level headers.
Sharing one flat order between every set of siblings let moving a group
reorder unrelated groups, for example swap two top-level groups when a third
was nested.

- Deleting groups captures, and Undo restores, their positions inside the
  service, so the delete command no longer reaches into the section order.
- The service owns the group section id scheme and exposes the ordered group
  tree, which the list reads instead of rebuilding trees and resolving the
  order of subgroups itself.
- The list has one nesting policy and one row depth method. A done chat that
  is listed in the Done section no longer steps in for its session's group.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
roblourens
roblourens previously approved these changes Oct 20, 2026
The nested group path label uses "›" as its separator, which the hygiene
unicode check rejects unless the line opts in.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Resolve the conflict with #342848 in sessionsList.test.ts by keeping both
new suites, and persist bulk group collapse state under the shared group
section id.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Bring in grouping by repository (#342997). Repository sections join the
top-level header order next to workspace sections, so the nesting-aware
header drop treats them as top-level headers too, and promotes a moved
repository section like a workspace section.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Drag a top-level group and then a subgroup through the list with the real
groups and section order services, starting from a stored order that mixes
the levels like the one the first version of nested groups wrote. Each
group's subgroups keep their own order. The drag helper is shared with the
existing nested drag test.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@benibenj
Benjamin Christopher Simmonds (benibenj) merged commit f75da50 into main Oct 20, 2026
58 checks passed
@benibenj
Benjamin Christopher Simmonds (benibenj) deleted the agents/sessions-list-nested-custom-groups branch October 20, 2026 23:43
@vs-code-engineering vs-code-engineering Bot added this to the 1.144.0 milestone Oct 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.

5 participants