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 31c8eb555dcd..57a92af41df8 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 @@ -18,6 +18,20 @@ import { DialogSessionDeleteFailed } from "./dialog-session-delete-failed" import { WorkspaceLabel } from "./workspace-label" import { useCommandShortcut } from "../keymap" +type SessionListFilter = { + scope?: "project" + path?: string +} + +export function createDialogSessionListQuery(input: { query: string; filter: SessionListFilter }) { + return { + ...input.filter, + roots: true, + limit: input.query ? 30 : 100, + ...(input.query ? { search: input.query } : {}), + } +} + export function DialogSessionList() { const dialog = useDialog() const route = useRoute() @@ -30,17 +44,16 @@ export function DialogSessionList() { const [search, setSearch] = createDebouncedSignal("", 150) const deleteHint = useCommandShortcut("session.delete") - const [searchResults, { refetch }] = createResource( + const [rootSessions, { refetch }] = createResource( () => ({ query: search(), filter: sync.session.query() }), async (input) => { - if (!input.query) return undefined - const result = await sdk.client.session.list({ search: input.query, limit: 30, ...input.filter }) + const result = await sdk.client.session.list(createDialogSessionListQuery(input)) return result.data ?? [] }, ) const currentSessionID = createMemo(() => (route.data.type === "session" ? route.data.sessionID : undefined)) - const sessions = createMemo(() => searchResults() ?? sync.data.session) + const sessions = createMemo(() => rootSessions() ?? sync.data.session.filter((x) => x.parentID === undefined)) function recover(session: NonNullable[number]>) { const workspace = project.workspace.get(session.workspaceID!) @@ -96,7 +109,7 @@ export function DialogSessionList() { } await project.workspace.sync() await sync.session.refresh() - if (search()) await refetch() + await refetch() if (info?.workspaceID === session.workspaceID) { route.navigate({ type: "home" }) } @@ -230,7 +243,7 @@ export function DialogSessionList() { if (status && status !== "connected") { await sync.session.refresh() } - if (search()) await refetch() + await refetch() setToDelete(undefined) return } diff --git a/packages/opencode/test/cli/tui/dialog-session-list.test.ts b/packages/opencode/test/cli/tui/dialog-session-list.test.ts new file mode 100644 index 000000000000..03fcac8947ac --- /dev/null +++ b/packages/opencode/test/cli/tui/dialog-session-list.test.ts @@ -0,0 +1,16 @@ +import { expect, test } from "bun:test" +import { createDialogSessionListQuery } from "@/cli/cmd/tui/component/dialog-session-list" + +test("dialog session list requests root sessions for the default picker", () => { + const query = createDialogSessionListQuery({ query: "", filter: { scope: "project" } }) + + expect(query).toEqual({ scope: "project", roots: true, limit: 100 }) + expect("start" in query).toBe(false) + expect("search" in query).toBe(false) +}) + +test("dialog session search preserves scope filters while requesting root sessions", () => { + const query = createDialogSessionListQuery({ query: "deploy", filter: { path: "packages/opencode" } }) + + expect(query).toEqual({ path: "packages/opencode", roots: true, limit: 30, search: "deploy" }) +})