From 7fd7994ee04b3e5aeb1f5db3eec6a703e04a0962 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pascal=20Andr=C3=A9?= Date: Sat, 25 Apr 2026 20:11:12 +0200 Subject: [PATCH] fix(app): show project sessions across worktrees --- packages/app/src/context/global-sync.test.ts | 20 +++++++++++++++++-- packages/app/src/context/global-sync.tsx | 17 ++++++++++------ .../src/context/global-sync/session-load.ts | 10 ++++++++-- packages/app/src/context/global-sync/types.ts | 3 ++- packages/app/src/pages/layout.tsx | 10 ++++++---- 5 files changed, 45 insertions(+), 15 deletions(-) diff --git a/packages/app/src/context/global-sync.test.ts b/packages/app/src/context/global-sync.test.ts index 93e9c4175557..7e4b3018cb6c 100644 --- a/packages/app/src/context/global-sync.test.ts +++ b/packages/app/src/context/global-sync.test.ts @@ -25,7 +25,7 @@ describe("pickDirectoriesToEvict", () => { describe("loadRootSessionsWithFallback", () => { test("uses limited roots query when supported", async () => { - const calls: Array<{ directory: string; roots: true; limit?: number }> = [] + const calls: Array<{ directory?: string; roots: true; limit?: number }> = [] const result = await loadRootSessionsWithFallback({ directory: "dir", @@ -42,7 +42,7 @@ describe("loadRootSessionsWithFallback", () => { }) test("falls back to full roots query on limited-query failure", async () => { - const calls: Array<{ directory: string; roots: true; limit?: number }> = [] + const calls: Array<{ directory?: string; roots: true; limit?: number }> = [] const result = await loadRootSessionsWithFallback({ directory: "dir", @@ -61,6 +61,22 @@ describe("loadRootSessionsWithFallback", () => { { directory: "dir", roots: true }, ]) }) + + test("omits directory filter for project-wide loads", async () => { + const calls: Array<{ directory?: string; roots: true; limit?: number }> = [] + + await loadRootSessionsWithFallback({ + directory: "dir", + filterDirectory: false, + limit: 10, + list: async (query) => { + calls.push(query) + return { data: [] } + }, + }) + + expect(calls).toEqual([{ roots: true, limit: 10 }]) + }) }) describe("estimateRootSessionTotal", () => { diff --git a/packages/app/src/context/global-sync.tsx b/packages/app/src/context/global-sync.tsx index 86496bad50c2..c242141ce596 100644 --- a/packages/app/src/context/global-sync.tsx +++ b/packages/app/src/context/global-sync.tsx @@ -41,7 +41,10 @@ type GlobalStore = { } export const loadSessionsQuery = (directory: string) => - queryOptions({ queryKey: [directory, "loadSessions"], queryFn: skipToken }) + queryOptions({ queryKey: [directory, "loadSessions", true], queryFn: skipToken }) + +const sessionLoadQuery = (directory: string, filterDirectory: boolean) => + queryOptions({ queryKey: [directory, "loadSessions", filterDirectory], queryFn: skipToken }) function createGlobalSync() { const globalSDK = useGlobalSDK() @@ -52,7 +55,7 @@ function createGlobalSync() { const sdkCache = new Map() const booting = new Map>() const sessionLoads = new Map>() - const sessionMeta = new Map() + const sessionMeta = new Map() const [globalStore, setGlobalStore] = createStore({ ready: false, @@ -146,14 +149,15 @@ function createGlobalSync() { return sdk } - async function loadSessions(directory: string) { + async function loadSessions(directory: string, options?: { filterDirectory?: boolean }) { + const filterDirectory = options?.filterDirectory ?? true const pending = sessionLoads.get(directory) if (pending) return pending children.pin(directory) const [store, setStore] = children.child(directory, { bootstrap: false }) const meta = sessionMeta.get(directory) - if (meta && meta.limit >= store.limit) { + if (meta && meta.filterDirectory === filterDirectory && meta.limit >= store.limit) { const next = trimSessions(store.session, { limit: store.limit, permission: store.permission, @@ -169,10 +173,11 @@ function createGlobalSync() { const limit = Math.max(store.limit + SESSION_RECENT_LIMIT, SESSION_RECENT_LIMIT) const promise = queryClient .fetchQuery({ - ...loadSessionsQuery(directory), + ...sessionLoadQuery(directory, filterDirectory), queryFn: () => loadRootSessionsWithFallback({ directory, + filterDirectory, limit, list: (query) => globalSDK.client.session.list(query), }) @@ -199,7 +204,7 @@ function createGlobalSync() { setStore("session", reconcile(sessions, { key: "id" })) cleanupDroppedSessionCaches(store, setStore, sessions, setSessionTodo) }) - sessionMeta.set(directory, { limit }) + sessionMeta.set(directory, { filterDirectory, limit }) }) .catch((err) => { console.error("Failed to load sessions", err) diff --git a/packages/app/src/context/global-sync/session-load.ts b/packages/app/src/context/global-sync/session-load.ts index 3693dcb460de..246d70d82c7e 100644 --- a/packages/app/src/context/global-sync/session-load.ts +++ b/packages/app/src/context/global-sync/session-load.ts @@ -1,15 +1,21 @@ import type { RootLoadArgs } from "./types" export async function loadRootSessionsWithFallback(input: RootLoadArgs) { + const query = (limit?: number) => ({ + ...(input.filterDirectory === false ? {} : { directory: input.directory }), + roots: true as const, + ...(limit ? { limit } : {}), + }) + try { - const result = await input.list({ directory: input.directory, roots: true, limit: input.limit }) + const result = await input.list(query(input.limit)) return { data: result.data, limit: input.limit, limited: true, } as const } catch { - const result = await input.list({ directory: input.directory, roots: true }) + const result = await input.list(query()) return { data: result.data, limit: input.limit, diff --git a/packages/app/src/context/global-sync/types.ts b/packages/app/src/context/global-sync/types.ts index e3ec83c5eeb3..1d29d89bd1f0 100644 --- a/packages/app/src/context/global-sync/types.ts +++ b/packages/app/src/context/global-sync/types.ts @@ -119,8 +119,9 @@ export type DisposeCheck = { export type RootLoadArgs = { directory: string + filterDirectory?: boolean limit: number - list: (query: { directory: string; roots: true; limit?: number }) => Promise<{ data?: Session[] }> + list: (query: { directory?: string; roots: true; limit?: number }) => Promise<{ data?: Session[] }> } export type RootLoadResult = { diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx index d9ce87a02e90..888a423289bd 100644 --- a/packages/app/src/pages/layout.tsx +++ b/packages/app/src/pages/layout.tsx @@ -1824,10 +1824,12 @@ export default function Layout(props: ParentProps) { return } - const next = new Set(dirs) - for (const directory of next) { - if (loadedSessionDirs.has(directory)) continue - void globalSync.project.loadSessions(directory) + const filterDirectory = workspaceSetting() + const next = new Set(dirs.map((directory) => `${directory}\n${filterDirectory}`)) + for (const directory of dirs) { + const key = `${directory}\n${filterDirectory}` + if (loadedSessionDirs.has(key)) continue + void globalSync.project.loadSessions(directory, { filterDirectory }) } loadedSessionDirs.clear()