import { describe, expect, test } from "bun:test" import type { Message, Part, SessionStatus } from "@opencode-ai/sdk/v2/client" import type { Session } from "@opencode-ai/sdk/v2" import { getReconnectCandidateSessionIds } from "./reconnect-recovery" function createSession(id: string, overrides: Partial = {}): Session { return { id, title: id, time: { created: 1, updated: 1 }, version: "1", ...overrides, } as Session } function createAssistantMessage(id: string, sessionID: string, completed?: number): Message { return { id, sessionID, role: "assistant", time: completed ? { created: 1, updated: 1, completed } : { created: 1, updated: 1 }, parts: [], } as unknown as Message } function createPart(id: string, messageID: string): Part { return { id, messageID, sessionID: "active", type: "text", text: "done" } as Part } describe("getReconnectCandidateSessionIds", () => { test("includes non-idle, incomplete assistant, and parent sessions", () => { const busyStatus = { type: "busy" } as SessionStatus expect(getReconnectCandidateSessionIds({ session: [ createSession("busy"), createSession("child", { parentID: "parent" }), createSession("parent"), createSession("incomplete"), ], session_status: { busy: busyStatus }, message: { incomplete: [createAssistantMessage("m-1", "incomplete")], }, }).sort()).toEqual(["busy", "incomplete", "parent"]) }) test("includes the currently viewed session even when it looks idle and complete", () => { expect(getReconnectCandidateSessionIds({ session: [createSession("active")], session_status: { active: { type: "idle" } as SessionStatus }, message: { active: [createAssistantMessage("m-1", "active", 1)], }, part: { "m-1": [createPart("p-1", "m-1")], }, }, { directory: "/repo", viewedSession: { directory: "/repo", sessionId: "active" }, }).sort()).toContain("active") }) test("includes completed assistant sessions when the latest assistant parts are missing", () => { expect(getReconnectCandidateSessionIds({ session: [createSession("blank")], session_status: { blank: { type: "idle" } as SessionStatus }, message: { blank: [createAssistantMessage("m-1", "blank", 1)], }, part: {}, })).toEqual(["blank"]) }) test("does not include a viewed session from another directory", () => { expect(getReconnectCandidateSessionIds({ session: [createSession("active")], session_status: { active: { type: "idle" } as SessionStatus }, message: { active: [createAssistantMessage("m-1", "active", 1)], }, part: { "m-1": [createPart("p-1", "m-1")], }, }, { directory: "/repo-a", viewedSession: { directory: "/repo-b", sessionId: "active" }, }).sort()).not.toContain("active") }) })