diff --git a/src/databases/officer_db/src/chat-session-cleanup.ts b/src/databases/officer_db/src/chat-session-cleanup.ts index b287a4b1..a181c2aa 100644 --- a/src/databases/officer_db/src/chat-session-cleanup.ts +++ b/src/databases/officer_db/src/chat-session-cleanup.ts @@ -1,4 +1,4 @@ -import { inArray } from 'drizzle-orm'; +import { inArray, sql } from 'drizzle-orm'; import { db } from './db'; import { chatSessionEvents } from './chat-events/schema'; import { chatNotificationPrefs } from './chat-notification-prefs/schema'; @@ -66,7 +66,27 @@ export async function forgetChatSessions({ transcriptIds, sessionKeys = [] }: Fo await db.delete(chatSessionModels).where(inArray(chatSessionModels.sessionId, transcriptIds)); await db.delete(chatNotificationPrefs).where(inArray(chatNotificationPrefs.sessionId, transcriptIds)); } - if (sessionKeys.length) { + /* + Events, resolved from the table's OWN data rather than from the caller's key. + + `sessionKeys` is a fast path and not a reliable one: the caller gets it from an in-memory map that is + empty for any session created before the last restart, so the first real test of this deleted everything + except the events — the key came back null and the list was empty. A cleanup that only works for + conversations younger than the process is not a cleanup. + + So ask the rows. Every event carries `claudeSessionId` in its payload, which is durable, survives any + restart, and is the one place the two id spaces are written down together. One id inside the JSON is + enough to resolve the key for all of that session's rows, including the events that carry no id of their + own — which is most of them. + */ + if (transcriptIds.length) { + const keysFromEvents = await db + .selectDistinct({ sessionId: chatSessionEvents.sessionId }) + .from(chatSessionEvents) + .where(inArray(sql`${chatSessionEvents.event}->>'claudeSessionId'`, transcriptIds)); + const keys = [...new Set([...sessionKeys, ...keysFromEvents.map((r) => r.sessionId)])]; + if (keys.length) await db.delete(chatSessionEvents).where(inArray(chatSessionEvents.sessionId, keys)); + } else if (sessionKeys.length) { await db.delete(chatSessionEvents).where(inArray(chatSessionEvents.sessionId, sessionKeys)); } }