diff --git a/src/session/index.ts b/src/session/index.ts index 8bb51313d..9c1bccc2f 100644 --- a/src/session/index.ts +++ b/src/session/index.ts @@ -244,7 +244,10 @@ export async function listSessions(cwd: string, home: string = homedir()): Promi }); continue; } - // TUI sessions persist conversation under context/ before run.json exists. + // A session directory with context/ but no readable run.json never + // reached its first saveState call (see src/tui/runner.ts's early + // "running" write) and therefore isn't actually running: report it as + // crashed rather than fabricating liveness. try { const dirStat = await stat(sessionDir(cwd, entry, home)); await stat(sessionContextDir(cwd, entry, home)); @@ -252,7 +255,7 @@ export async function listSessions(cwd: string, home: string = homedir()): Promi sessionId: entry, task: "(conversation)", startedAt: dirStat.birthtimeMs > 0 ? dirStat.birthtimeMs : dirStat.mtimeMs, - status: "running", + status: "crashed", }); } catch { // Not a resumable session directory. diff --git a/src/session/list-sessions.test.ts b/src/session/list-sessions.test.ts index 85b10f0a0..ac869334f 100644 --- a/src/session/list-sessions.test.ts +++ b/src/session/list-sessions.test.ts @@ -30,6 +30,15 @@ test("listSessions includes TUI sessions with context/ but no run.json", async ( expect(row?.task).toBe("Untitled session"); }); +test("listSessions reports crashed, not running, for a session with no readable run.json", async () => { + const sessionId = generateSessionId(); + await initSessionDir(cwd, sessionId, home); + const listed = await listSessions(cwd, home); + const row = listed.find((s) => s.sessionId === sessionId); + expect(row?.status).not.toBe("running"); + expect(row?.status).toBe("crashed"); +}); + test("listSessions prefers run.json task title when present", async () => { const sessionId = generateSessionId(); await initSessionDir(cwd, sessionId, home);