Skip to content

Commit ecacaf2

Browse files
committed
fix(tui): filter session list to root sessions only
Bootstrap fetch and search in session list dialog now pass roots: true, so the default limit of 100 applies to root sessions only instead of root + child mixed. Fixes truncated session list for projects with many subagent sessions.
1 parent 9b0f299 commit ecacaf2

3 files changed

Lines changed: 58 additions & 2 deletions

File tree

‎packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export function DialogSessionList() {
2626

2727
const [searchResults] = createResource(search, async (query) => {
2828
if (!query) return undefined
29-
const result = await sdk.client.session.list({ search: query, limit: 30 })
29+
const result = await sdk.client.session.list({ search: query, limit: 30, roots: true })
3030
return result.data ?? []
3131
})
3232

‎packages/opencode/src/cli/cmd/tui/context/sync.tsx‎

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
350350
console.log("bootstrapping")
351351
const start = Date.now() - 30 * 24 * 60 * 60 * 1000
352352
const sessionListPromise = sdk.client.session
353-
.list({ start: start })
353+
.list({ start: start, roots: true })
354354
.then((x) => (x.data ?? []).toSorted((a, b) => a.id.localeCompare(b.id)))
355355

356356
// blocking - include session.list when continuing a session
@@ -432,6 +432,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
432432
})
433433

434434
const fullSyncedSessions = new Set<string>()
435+
const treeSyncedRoots = new Set<string>()
435436
const result = {
436437
data: store,
437438
set: setStore,
@@ -480,6 +481,60 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
480481
)
481482
fullSyncedSessions.add(sessionID)
482483
},
484+
async syncTree(sessionID: string) {
485+
// Walk up to find the root session
486+
let rootID = sessionID
487+
const visited = new Set<string>()
488+
while (true) {
489+
if (visited.has(rootID)) break
490+
visited.add(rootID)
491+
const match = Binary.search(store.session, rootID, (s) => s.id)
492+
if (!match.found) break
493+
const parentID = store.session[match.index].parentID
494+
if (!parentID) break
495+
const parentMatch = Binary.search(store.session, parentID, (s) => s.id)
496+
if (parentMatch.found) {
497+
rootID = parentID
498+
continue
499+
}
500+
// Parent not in store — fetch it
501+
const res = await sdk.client.session.get({ sessionID: parentID }).catch(() => undefined)
502+
if (!res?.data) break
503+
setStore(
504+
produce((draft) => {
505+
const idx = Binary.search(draft.session, res.data!.id, (s) => s.id)
506+
if (!idx.found) draft.session.splice(idx.index, 0, res.data!)
507+
}),
508+
)
509+
rootID = parentID
510+
}
511+
512+
if (treeSyncedRoots.has(rootID)) return
513+
treeSyncedRoots.add(rootID)
514+
515+
// BFS from root — load all descendants level by level
516+
const queue = [rootID]
517+
while (queue.length > 0) {
518+
const level = [...queue]
519+
queue.length = 0
520+
const results = await Promise.all(
521+
level.map((id) => sdk.client.session.children({ sessionID: id }).catch(() => undefined)),
522+
)
523+
const all = results.flatMap((r) => r?.data ?? [])
524+
if (all.length === 0) break
525+
setStore(
526+
produce((draft) => {
527+
for (const child of all) {
528+
const idx = Binary.search(draft.session, child.id, (s) => s.id)
529+
if (!idx.found) draft.session.splice(idx.index, 0, child)
530+
}
531+
}),
532+
)
533+
for (const child of all) {
534+
queue.push(child.id)
535+
}
536+
}
537+
},
483538
},
484539
bootstrap,
485540
}

‎packages/opencode/src/cli/cmd/tui/routes/session/index.tsx‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ export function Session() {
229229
})
230230
return navigate({ type: "home" })
231231
})
232+
sync.session.syncTree(route.sessionID).catch(() => {})
232233
})
233234

234235
const toast = useToast()

0 commit comments

Comments
 (0)