feat(FN-1146): add configurable AI session cleanup lifecycle

- Add aiSessionTtlMs and aiSessionCleanupIntervalMs project settings with defaults and bounds for cleanup scheduling
- Extend AiSessionStore cleanup to expire stale in-progress sessions, emit deletion events, and support start/stop scheduled cleanup loops
- Wire server startup/shutdown to load cleanup settings and manage the scheduled ai_sessions sweep lifecycle
- Align planning, subtask breakdown, and mission interview in-memory session retention to a 7-day TTL with shared deletion-driven cleanup
- Update schema/tests/docs for migration v15 ai_sessions indexing and end-to-end cleanup/TTL behavior stability
This commit is contained in:
gsxdsm
2026-04-08 06:44:09 -07:00
parent 220c269b0d
commit 00d5f36240
12 changed files with 506 additions and 144 deletions

View File

@@ -19,6 +19,7 @@ import {
parseAgentResponse,
generateSubtasksFromPlanning,
formatInterviewQA,
SESSION_TTL_MS,
} from "./planning.js";
import type { PlanningQuestion, PlanningSummary } from "@fusion/core";
@@ -483,23 +484,20 @@ describe("planning module", () => {
});
describe("session TTL", () => {
it("sessions expire after TTL", async () => {
it("uses a 7-day TTL constant", () => {
expect(SESSION_TTL_MS).toBe(7 * 24 * 60 * 60 * 1000);
});
it("does not expire sessions within the old 30-minute window", async () => {
vi.useFakeTimers({ shouldAdvanceTime: true });
try {
const mockIp = getUniqueIp();
const { sessionId } = await createSession(mockIp, initialPlan, undefined, TEST_ROOT_DIR);
// Verify session exists
expect(getSession(sessionId)).toBeDefined();
// Advance time by 31 minutes
// Advance beyond the old 30-minute TTL used prior to FN-1146.
vi.advanceTimersByTime(31 * 60 * 1000);
// Trigger cleanup by creating a new session
await createSession(getUniqueIp(), "Another plan", undefined, TEST_ROOT_DIR);
// Note: Session should be expired after cleanup runs
// We can't directly verify as cleanup is async
expect(getSession(sessionId)).toBeDefined();
} finally {
vi.useRealTimers();
}