fix(FN-2177): exclude terminal AI sessions from background list

- Limit background session inclusion to active statuses (generating and awaiting_input)
- Remove sessions from local state when SSE updates deliver complete or error terminal statuses
- Update useBackgroundSessions tests to assert terminal sessions are filtered out and removed on SSE updates
- Adjust session count expectations to reflect active-only tracking
This commit is contained in:
Fusion
2026-04-19 14:22:48 -07:00
committed by gsxdsm
parent ff2813ce4a
commit 66ad0769c4
2 changed files with 102 additions and 11 deletions

View File

@@ -74,8 +74,6 @@ describe("useBackgroundSessions", () => {
await waitFor(() => {
expect(result.current.sessions.map((session) => session.id).sort()).toEqual([
"s-awaiting",
"s-complete",
"s-error",
"s-generating",
]);
});
@@ -147,6 +145,97 @@ describe("useBackgroundSessions", () => {
});
});
it("removes session when SSE delivers complete status", async () => {
const { result } = renderHook(() => useBackgroundSessions());
await waitFor(() => {
expect(MockEventSource.instances.length).toBeGreaterThan(0);
});
const eventSource = MockEventSource.instances[0]!;
act(() => {
eventSource._emit(
"ai_session:updated",
makeSession({
id: "terminal-complete",
type: "planning",
status: "generating",
updatedAt: "2026-04-08T00:00:01.000Z",
}),
);
});
await waitFor(() => {
expect(result.current.generating).toBe(1);
expect(result.current.sessions.map((session) => session.id)).toEqual(["terminal-complete"]);
expect(result.current.planningSessions.map((session) => session.id)).toEqual(["terminal-complete"]);
});
act(() => {
eventSource._emit(
"ai_session:updated",
makeSession({
id: "terminal-complete",
type: "planning",
status: "complete",
updatedAt: "2026-04-08T00:00:02.000Z",
}),
);
});
await waitFor(() => {
expect(result.current.sessions).toEqual([]);
expect(result.current.planningSessions).toEqual([]);
expect(result.current.generating).toBe(0);
});
});
it("removes session when SSE delivers error status", async () => {
const { result } = renderHook(() => useBackgroundSessions());
await waitFor(() => {
expect(MockEventSource.instances.length).toBeGreaterThan(0);
});
const eventSource = MockEventSource.instances[0]!;
act(() => {
eventSource._emit(
"ai_session:updated",
makeSession({
id: "terminal-error",
type: "planning",
status: "generating",
updatedAt: "2026-04-08T00:00:01.000Z",
}),
);
});
await waitFor(() => {
expect(result.current.sessions.map((session) => session.id)).toEqual(["terminal-error"]);
expect(result.current.planningSessions.map((session) => session.id)).toEqual(["terminal-error"]);
});
act(() => {
eventSource._emit(
"ai_session:updated",
makeSession({
id: "terminal-error",
type: "planning",
status: "error",
updatedAt: "2026-04-08T00:00:02.000Z",
}),
);
});
await waitFor(() => {
expect(result.current.sessions).toEqual([]);
expect(result.current.planningSessions).toEqual([]);
expect(result.current.generating).toBe(0);
});
});
it("removes sessions when ai_session:deleted SSE event arrives", async () => {
mockFetchAiSessions.mockResolvedValueOnce([
makeSession({ id: "delete-me", status: "awaiting_input" }),
@@ -259,8 +348,11 @@ describe("useBackgroundSessions", () => {
await waitFor(() => {
expect(result.current.generating).toBe(1);
expect(result.current.needsInput).toBe(1);
expect(result.current.planningSessions.map((session) => session.id).sort()).toEqual([
"count-error-plan",
expect(result.current.planningSessions.map((session) => session.id)).toEqual([
"count-generating",
]);
expect(result.current.sessions.map((session) => session.id).sort()).toEqual([
"count-awaiting",
"count-generating",
]);
});

View File

@@ -28,12 +28,7 @@ function parseTimestamp(updatedAt: string | undefined): number {
}
function shouldIncludeSession(session: AiSessionSummary): boolean {
return (
session.status === "generating" ||
session.status === "awaiting_input" ||
session.status === "complete" ||
session.status === "error"
);
return session.status === "generating" || session.status === "awaiting_input";
}
export function useBackgroundSessions(projectId?: string): UseBackgroundSessionsResult {
@@ -152,7 +147,11 @@ export function useBackgroundSessions(projectId?: string): UseBackgroundSessions
sessionTimestampsRef.current.set(updated.id, eventTimestamp);
const idx = prev.findIndex((s) => s.id === updated.id);
if (updated.status === "complete" || updated.status === "error") {
return prev.filter((session) => session.id !== updated.id);
}
const idx = prev.findIndex((session) => session.id === updated.id);
if (idx >= 0) {
const next = [...prev];
next[idx] = updated;