Adds a group-by-project toggle to the Sessions header. When ON, the
flat session list becomes a grouped list — one section per project,
preview-capped at 6 agents per project with "Show N more" / "Show less"
controls per group. When OFF, the flat list stays as before.
Architecture preserves the Phase 7b perf model:
- Controller (useSidebarSessionsController) owns groupByProject and
expandedProjects state. Expansion resets on grouping-off, host change,
and filter change. Group-by persists across hosts as a UI preference.
- A new useOrderedAgentProjectShape selector reactively subscribes to
per-agent cwd via useStoreWithEqualityFn(..., shallow). When project
assignments don't change, the array reference stays stable; when an
agent's project changes, the parent re-derives groups. Shape objects
are reused across renders when project fields are unchanged.
- deriveGroupedSidebarSessions takes the resolved shape directly (no
callback indirection) and returns ordered groups with visible/hidden
ids based on per-project expansion state.
- Header and Footer components are React.memo'd with primitive props
only, so they don't re-render on irrelevant agent updates. Per-row
status indicators continue to subscribe via the shared tab descriptor
path, isolated from parent renders.
- Project ordering: first-occurrence in the createdAt-desc id list,
matching the user's mental model of "most-recent project first" and
avoiding a per-render activity-timestamp computation.
Visual primitive shared between the workspace tree project row and the
new Sessions group header (packages/app/src/components/sidebar/
sidebar-project-row-visual.tsx) — no two systems for project visuals.
The workspace tree wraps it with its own chevron/status; the Sessions
header uses it bare.
Tests added: state-machine cases on the controller, pure-helper coverage
on grouping derivation, render-boundary stability for grouped data
references across irrelevant updates and reactivity on project changes,
and a footer click test.
Adds a Notion-style pill toggle at the top of the always-visible sidebar
that switches between Workspaces (current tree) and Sessions (flat
live-updating list of agents across the daemon). Sessions are sorted by
createdAt desc (stable, tie-broken by id) and filtered to agents whose
cwd maps to an opened workspace; clicking a row focuses the matching
workspace tab via prepareWorkspaceTab + navigateToPreparedWorkspaceTab.
The existing "Sessions" sidebar row is renamed to "History" (same
destination).
Lives in its own module at packages/app/src/sidebar-sessions/ with the
sidebar shell pieces extracted to packages/app/src/components/sidebar/.
Render performance is treated as first-class: parent subscribes to an
ordered id list with shallow array equality (useAggregatedAgentIds),
each row uses a per-agent shallow Zustand selector, the inactive view
is unmounted on toggle, and the filter dropdown subscription is gated
to Sessions mode. Filter state resets on host change. Agents whose cwd
doesn't map to an opened workspace are hidden from both list and
dropdown options.
Includes a Playwright user-journey spec and unit/integration tests for
the new ids hook, slice selector, render boundaries, navigation,
filtering, and the sidebar-sessions controller.