From 16fa43f1cea3066803689b91b6487c114e322a98 Mon Sep 17 00:00:00 2001 From: heimoshuiyu Date: Sun, 26 Apr 2026 03:43:46 +0800 Subject: [PATCH 1/2] fix: move session roots filter to SQL layer to prevent child sessions from crowding out the LIMIT Previously the TUI fetched all sessions (including subagent child sessions) with LIMIT 100 and filtered client-side. This meant child sessions could fill the limit, leaving few or no root sessions visible. Now passes `roots: true` to the backend so `parent_id IS NULL` is applied in the SQL query, reserving the full limit for root sessions. Related #16270, #20238 --- .../src/cli/cmd/tui/component/dialog-session-list.tsx | 3 +-- packages/opencode/src/cli/cmd/tui/context/sync.tsx | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx index 7260a14f9c75..f7c07a7deec0 100644 --- a/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx @@ -34,7 +34,7 @@ export function DialogSessionList() { const [searchResults, { refetch }] = createResource(search, async (query) => { if (!query) return undefined - const result = await sdk.client.session.list({ search: query, limit: 30 }) + const result = await sdk.client.session.list({ search: query, limit: 30, roots: true }) return result.data ?? [] }) @@ -112,7 +112,6 @@ export function DialogSessionList() { const options = createMemo(() => { const today = new Date().toDateString() return sessions() - .filter((x) => x.parentID === undefined) .toSorted((a, b) => { const updatedDay = new Date(b.time.updated).setHours(0, 0, 0, 0) - new Date(a.time.updated).setHours(0, 0, 0, 0) if (updatedDay !== 0) return updatedDay diff --git a/packages/opencode/src/cli/cmd/tui/context/sync.tsx b/packages/opencode/src/cli/cmd/tui/context/sync.tsx index d35deb0b62a7..9b3f6eca3984 100644 --- a/packages/opencode/src/cli/cmd/tui/context/sync.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/sync.tsx @@ -362,7 +362,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ } const start = Date.now() - 30 * 24 * 60 * 60 * 1000 const sessionListPromise = sdk.client.session - .list({ start: start }) + .list({ start: start, roots: true }) .then((x) => (x.data ?? []).toSorted((a, b) => a.id.localeCompare(b.id))) // blocking - include session.list when continuing a session @@ -482,7 +482,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ async refresh() { const start = Date.now() - 30 * 24 * 60 * 60 * 1000 const list = await sdk.client.session - .list({ start }) + .list({ start, roots: true }) .then((x) => (x.data ?? []).toSorted((a, b) => a.id.localeCompare(b.id))) setStore("session", reconcile(list)) }, From b6af6ca8d2fa6a65d6c56a8e8c868cbf313188a2 Mon Sep 17 00:00:00 2001 From: heimoshuiyu Date: Sun, 26 Apr 2026 17:14:23 +0800 Subject: [PATCH 2/2] fix: restore client-side parentID filter to guard against realtime subagent session leaks The d7dfc720d commit moved the roots filter to SQL (roots: true) but removed the client-side .filter((x) => x.parentID === undefined) guard. While SQL filtering correctly reserves the LIMIT quota for root sessions, subagent sessions can still enter the store via session.updated SSE events which bypass the SQL layer. Adding the filter back as a defensive measure. --- .../opencode/src/cli/cmd/tui/component/dialog-session-list.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx index f7c07a7deec0..ee011805928f 100644 --- a/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx @@ -112,6 +112,7 @@ export function DialogSessionList() { const options = createMemo(() => { const today = new Date().toDateString() return sessions() + .filter((x) => x.parentID === undefined) .toSorted((a, b) => { const updatedDay = new Date(b.time.updated).setHours(0, 0, 0, 0) - new Date(a.time.updated).setHours(0, 0, 0, 0) if (updatedDay !== 0) return updatedDay