@@ -11,17 +11,25 @@ const logger = createLogger('ChatCleanup')
1111
1212const COPILOT_CLEANUP_BATCH_SIZE = 1000
1313
14+ /**
15+ * Only storage in these contexts is tied to chat/task lifecycle. Workspace
16+ * files, execution logs, knowledge bases, profile pictures, etc. are owned by
17+ * other subsystems and must never be touched by chat cleanup — even if a row
18+ * somehow ends up with `chatId` set through a future flow.
19+ */
20+ const CHAT_SCOPED_CONTEXTS = [ 'copilot' , 'mothership' ] as const satisfies readonly StorageContext [ ]
21+ type ChatScopedContext = ( typeof CHAT_SCOPED_CONTEXTS ) [ number ]
22+
1423interface FileRef {
1524 key : string
16- context : StorageContext
25+ context : ChatScopedContext
1726}
1827
1928/**
2029 * Collect all file storage keys associated with the given chat IDs.
2130 * Two sources:
22- * 1. workspaceFiles rows with chatId FK — includes the storage context
23- * 2. fileAttachments[].key inside copilotChats.messages JSONB — these are
24- * uploaded with context='copilot'
31+ * 1. workspaceFiles rows with chatId FK — filtered to chat-scoped contexts only
32+ * 2. fileAttachments[].key inside copilotChats.messages JSONB — all copilot uploads
2533 */
2634export async function collectChatFiles ( chatIds : string [ ] ) : Promise < FileRef [ ] > {
2735 const files : FileRef [ ] = [ ]
@@ -33,7 +41,13 @@ export async function collectChatFiles(chatIds: string[]): Promise<FileRef[]> {
3341 db
3442 . select ( { key : workspaceFiles . key , context : workspaceFiles . context } )
3543 . from ( workspaceFiles )
36- . where ( and ( inArray ( workspaceFiles . chatId , chatIds ) , isNull ( workspaceFiles . deletedAt ) ) ) ,
44+ . where (
45+ and (
46+ inArray ( workspaceFiles . chatId , chatIds ) ,
47+ isNull ( workspaceFiles . deletedAt ) ,
48+ inArray ( workspaceFiles . context , [ ...CHAT_SCOPED_CONTEXTS ] )
49+ )
50+ ) ,
3751 db
3852 . select ( { messages : copilotChats . messages } )
3953 . from ( copilotChats )
@@ -43,7 +57,7 @@ export async function collectChatFiles(chatIds: string[]): Promise<FileRef[]> {
4357 for ( const f of linkedFiles ) {
4458 if ( ! seen . has ( f . key ) ) {
4559 seen . add ( f . key )
46- files . push ( { key : f . key , context : f . context as StorageContext } )
60+ files . push ( { key : f . key , context : f . context as ChatScopedContext } )
4761 }
4862 }
4963
0 commit comments