diff --git a/packages/opencode/src/cli/cmd/run.ts b/packages/opencode/src/cli/cmd/run.ts index a55fd7f1b..99624b1e8 100644 --- a/packages/opencode/src/cli/cmd/run.ts +++ b/packages/opencode/src/cli/cmd/run.ts @@ -27,6 +27,7 @@ import { BashTool } from "../../tool/bash" import { Locale } from "../../util" import { AppRuntime } from "@/effect/app-runtime" import { createCompletionTracker, type CompletionTracker } from "./run-completion" +import { selectContinueSessionID } from "../continue-session" type ToolProps = { input: Tool.InferParameters @@ -365,7 +366,7 @@ export const RunCommand = cmd({ } async function session(sdk: OpencodeClient) { - const baseID = args.continue ? (await sdk.session.list()).data?.find((s) => !s.parentID)?.id : args.session + const baseID = args.continue ? selectContinueSessionID((await sdk.session.list()).data ?? []) : args.session if (baseID && args.fork) { const forked = await sdk.session.fork({ sessionID: baseID }) diff --git a/packages/opencode/src/cli/cmd/tui/app.tsx b/packages/opencode/src/cli/cmd/tui/app.tsx index a353b8b72..43dfac305 100644 --- a/packages/opencode/src/cli/cmd/tui/app.tsx +++ b/packages/opencode/src/cli/cmd/tui/app.tsx @@ -68,6 +68,7 @@ import { TuiConfig } from "@/cli/cmd/tui/config/tui" import { createTuiApi, TuiPluginRuntime, type RouteMap } from "./plugin" import { FormatError, FormatUnknownError } from "@/cli/error" import { isPlainTerminal } from "./util/terminal" +import { selectContinueSessionID } from "../../continue-session" import type { EventSource } from "./context/sdk" import { DialogVariant } from "./component/dialog-variant" @@ -378,9 +379,7 @@ function App(props: { onSnapshot?: () => Promise }) { createEffect(() => { // When using -c, session list is loaded in blocking phase, so we can navigate at "partial" if (continued || sync.status === "loading" || !args.continue) return - const match = sync.data.session - .toSorted((a, b) => b.time.updated - a.time.updated) - .find((x) => x.parentID === undefined)?.id + const match = selectContinueSessionID(sync.data.session) if (match) { continued = true if (args.fork) { diff --git a/packages/opencode/src/cli/continue-session.ts b/packages/opencode/src/cli/continue-session.ts new file mode 100644 index 000000000..978f98e82 --- /dev/null +++ b/packages/opencode/src/cli/continue-session.ts @@ -0,0 +1,16 @@ +const SYSTEM_CONTINUE_TITLES = new Set(["Auto Dream", "Auto Distill"]) + +type ContinueSessionCandidate = { + id: string + title?: string + parentID?: string + time?: { + updated?: number + } +} + +export function selectContinueSessionID(sessions: ContinueSessionCandidate[]) { + return sessions + .toSorted((a, b) => (b.time?.updated ?? 0) - (a.time?.updated ?? 0)) + .find((session) => session.parentID === undefined && !SYSTEM_CONTINUE_TITLES.has(session.title ?? ""))?.id +} diff --git a/packages/opencode/test/cli/continue-session.test.ts b/packages/opencode/test/cli/continue-session.test.ts new file mode 100644 index 000000000..fab898622 --- /dev/null +++ b/packages/opencode/test/cli/continue-session.test.ts @@ -0,0 +1,42 @@ +import { expect, test } from "bun:test" +import { selectContinueSessionID } from "../../src/cli/continue-session" + +test("selectContinueSessionID skips auto system sessions", () => { + expect( + selectContinueSessionID([ + { + id: "ses_auto_dream", + title: "Auto Dream", + time: { updated: 300 }, + }, + { + id: "ses_auto_distill", + title: "Auto Distill", + time: { updated: 200 }, + }, + { + id: "ses_work", + title: "Working session", + time: { updated: 100 }, + }, + ]), + ).toBe("ses_work") +}) + +test("selectContinueSessionID skips child sessions", () => { + expect( + selectContinueSessionID([ + { + id: "ses_child", + title: "checkpoint-writer", + parentID: "ses_parent", + time: { updated: 300 }, + }, + { + id: "ses_parent", + title: "Working session", + time: { updated: 100 }, + }, + ]), + ).toBe("ses_parent") +})