From be366f4a45430c4864cf0f57c2e5b2f37e4f2bf2 Mon Sep 17 00:00:00 2001 From: Benjamin Shafii Date: Sat, 7 Feb 2026 15:43:29 -0800 Subject: [PATCH] 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. --- packages/app/src/app/app.tsx | 21 +++++++++++++-------- packages/app/src/app/context/session.ts | 20 +++++++++++++++++++- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/packages/app/src/app/app.tsx b/packages/app/src/app/app.tsx index 611855d1a..6c66342e4 100644 --- a/packages/app/src/app/app.tsx +++ b/packages/app/src/app/app.tsx @@ -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) => ({ diff --git a/packages/app/src/app/context/session.ts b/packages/app/src/app/context/session.ts index d04f9fa1a..3725059ed 100644 --- a/packages/app/src/app/context/session.ts +++ b/packages/app/src/app/context/session.ts @@ -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)