diff --git a/apps/web/src/session-logic.test.ts b/apps/web/src/session-logic.test.ts index 4a113adeb..61758e962 100644 --- a/apps/web/src/session-logic.test.ts +++ b/apps/web/src/session-logic.test.ts @@ -10,6 +10,7 @@ import { describe, expect, it } from "vitest"; import { deriveActiveWorkStartedAt, deriveActivePlanState, + derivePhase, PROVIDER_OPTIONS, derivePendingApprovals, derivePendingUserInputs, @@ -21,6 +22,7 @@ import { hasToolActivityForTurn, isLatestTurnSettled, } from "./session-logic"; +import type { ThreadSession } from "./types"; function makeActivity(overrides: { id?: string; @@ -174,6 +176,21 @@ describe("derivePendingApprovals", () => { }); }); +describe("derivePhase", () => { + it("treats errored sessions as disconnected instead of ready", () => { + const session: ThreadSession = { + provider: "codex", + status: "error", + orchestrationStatus: "error", + createdAt: "2026-02-23T00:00:00.000Z", + updatedAt: "2026-02-23T00:00:01.000Z", + lastError: "Provider crashed", + }; + + expect(derivePhase(session)).toBe("disconnected"); + }); +}); + describe("derivePendingUserInputs", () => { it("tracks open structured prompts and removes resolved ones", () => { const activities: OrchestrationThreadActivity[] = [ diff --git a/apps/web/src/session-logic.ts b/apps/web/src/session-logic.ts index 7c3ea96e6..e1d9c5f18 100644 --- a/apps/web/src/session-logic.ts +++ b/apps/web/src/session-logic.ts @@ -865,7 +865,9 @@ export function inferCheckpointTurnCountByTurnId( } export function derivePhase(session: ThreadSession | null): SessionPhase { - if (!session || session.status === "closed") return "disconnected"; + if (!session || session.status === "closed" || session.status === "error") { + return "disconnected"; + } if (session.status === "connecting") return "connecting"; if (session.status === "running") return "running"; return "ready";