fix(app): scope session listing to workspace

Avoid fetching the global session list on workspace switches by passing directory/roots to session.list; reduces UI hangs and memory churn.
This commit is contained in:
Benjamin Shafii
2026-02-07 15:43:29 -08:00
parent f7f0abaa4d
commit be366f4a45
2 changed files with 32 additions and 9 deletions

View File

@@ -1551,17 +1551,22 @@ export default function App() {
}
}
const list = unwrap(await c.session.list());
const queryDirectory = (() => {
const trimmed = (directory ?? "").trim();
if (!trimmed) return undefined;
const unified = trimmed.replace(/\\/g, "/");
const withoutTrailing = unified.replace(/\/+$/, "");
return withoutTrailing || "/";
})();
// Fetch sessions scoped to the workspace directory to avoid loading the
// full global session list for every workspace.
const list = unwrap(await c.session.list({ directory: queryDirectory, roots: true }));
if (sidebarRefreshSeqByWorkspaceId[id] !== seq) return;
// `session.list()` can return sessions across multiple workspace roots.
// The dashboard sidebar shows sessions grouped by workspace, so we must
// filter by the workspace root to avoid every local workspace rendering the
// same global list.
// Defensive client-side filter in case upstream ignores the directory query.
const root = normalizeDirectoryPath(directory);
const filtered = root
? list.filter((session) => normalizeDirectoryPath(session.directory) === root)
: list;
const filtered = root ? list.filter((session) => normalizeDirectoryPath(session.directory) === root) : list;
const sorted = sortSessionsByActivity(filtered);
const items: SidebarSessionItem[] = sorted.map((session) => ({

View File

@@ -409,7 +409,25 @@ export function createSessionStore(options: {
async function loadSessions(scopeRoot?: string) {
const c = options.client();
if (!c) return;
const list = unwrap(await c.session.list());
// IMPORTANT: OpenCode's session.list() supports server-side filtering by directory.
// Use it to avoid fetching every session across every workspace root.
//
// Note: We intentionally normalize slashes + trailing separators but do NOT
// lowercase on Windows for the query value because the server does strict
// string equality against the stored session.directory.
const queryDirectory = (() => {
const trimmed = (scopeRoot ?? "").trim();
if (!trimmed) return undefined;
const unified = trimmed.replace(/\\/g, "/");
const withoutTrailing = unified.replace(/\/+$/, "");
return withoutTrailing || "/";
})();
const list = unwrap(await c.session.list({ directory: queryDirectory, roots: true }));
// Defensive client-side filter in case the server returns sessions spanning
// multiple roots (e.g. older servers or proxies).
const root = normalizeDirectoryPath(scopeRoot);
const filtered = root
? list.filter((session) => normalizeDirectoryPath(session.directory) === root)