fix: keep new sessions grouped by project

Preserves session directory metadata across live updates
Keeps desktop and mobile session lists in project groups
Adds regression coverage for session grouping
This commit is contained in:
Bohdan Triapitsyn
2026-06-03 14:51:17 +03:00
parent e120a379f9
commit 8e25bc4cce
12 changed files with 132 additions and 29 deletions
@@ -69,7 +69,7 @@ import {
formatProjectLabel,
normalizePath,
} from './sidebar/utils';
import { refreshGlobalSessions, resolveGlobalSessionDirectory, useGlobalSessionsStore } from '@/stores/useGlobalSessionsStore';
import { mergeSessionDirectoryMetadata, refreshGlobalSessions, resolveGlobalSessionDirectory, useGlobalSessionsStore } from '@/stores/useGlobalSessionsStore';
import { useRuntimeAPIs } from '@/hooks/useRuntimeAPIs';
import { useGitHubAuthStore } from '@/stores/useGitHubAuthStore';
import { subscribeOpenchamberEvents } from '@/lib/openchamberEvents';
@@ -332,7 +332,10 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
const sessions = React.useMemo(() => {
const liveById = new Map(liveSessions.map((session) => [session.id, session]));
const merged = globalActiveSessions.map((session) => liveById.get(session.id) ?? session);
const merged = globalActiveSessions.map((session) => {
const liveSession = liveById.get(session.id);
return liveSession ? mergeSessionDirectoryMetadata(liveSession, session) : session;
});
const seenIds = new Set(merged.map((session) => session.id));
liveSessions.forEach((session) => {
@@ -1,5 +1,6 @@
import React from 'react';
import type { Session } from '@opencode-ai/sdk/v2';
import { resolveGlobalSessionDirectory } from '@/stores/useGlobalSessionsStore';
import { dedupeSessionsById, isSessionRelatedToProject, normalizePath } from '../utils';
type WorktreeMeta = { path: string };
@@ -22,8 +23,7 @@ export const useProjectSessionLists = (args: Args) => {
const sessionsByDirectory = React.useMemo(() => {
const next = new Map<string, Session[]>();
sessions.forEach((session) => {
const directory = normalizePath((session as Session & { directory?: string | null }).directory ?? null)
?? normalizePath((session as Session & { project?: { worktree?: string | null } | null }).project?.worktree ?? null);
const directory = resolveGlobalSessionDirectory(session);
if (!directory) {
return;
}
@@ -11,6 +11,7 @@ import {
} from '../utils';
import { formatDirectoryName, formatPathForDisplay } from '@/lib/utils';
import { useI18n } from '@/lib/i18n';
import { resolveGlobalSessionDirectory } from '@/stores/useGlobalSessionsStore';
type Args = {
homeDirectory: string | null;
@@ -124,10 +125,7 @@ export const useSessionGrouping = (args: Args) => {
const getGroupKey = (session: Session) => {
if (session.time?.archived) return archivedKey;
const metadataPath = normalizePath(args.worktreeMetadata.get(session.id)?.path ?? null);
const sessionDirectory = normalizePath((session as Session & { directory?: string | null }).directory ?? null);
if (!metadataPath && !sessionDirectory) return archivedKey;
const fallbackDirectory = normalizePath((session as Session & { project?: { worktree?: string | null } | null }).project?.worktree ?? null);
const normalizedDir = metadataPath ?? sessionDirectory ?? fallbackDirectory;
const normalizedDir = metadataPath ?? resolveGlobalSessionDirectory(session);
if (!normalizedDir) return archivedKey;
if (normalizedDir !== normalizedProjectRoot && worktreeByPath.has(normalizedDir)) return normalizedDir;
if (normalizedDir === normalizedProjectRoot) return normalizedProjectRoot ?? '__project_root__';