@@ -1167,28 +1167,50 @@ export const chatsRouter = router({
11671167 /**
11681168 * Get file change stats for workspaces
11691169 * Parses messages from specified sub-chats and aggregates Edit/Write tool calls
1170- * REQUIRES openSubChatIds to avoid loading all sub-chats (performance optimization)
1170+ * Supports two modes:
1171+ * - openSubChatIds: query specific sub-chats (used by main sidebar)
1172+ * - chatIds: query all sub-chats for given chats (used by archive popover)
11711173 */
11721174 getFileStats : publicProcedure
1173- . input ( z . object ( { openSubChatIds : z . array ( z . string ( ) ) } ) )
1175+ . input ( z . object ( {
1176+ openSubChatIds : z . array ( z . string ( ) ) . optional ( ) ,
1177+ chatIds : z . array ( z . string ( ) ) . optional ( ) ,
1178+ } ) )
11741179 . query ( ( { input } ) => {
11751180 const db = getDatabase ( )
11761181
1177- // Early return if no sub-chats to check
1178- if ( input . openSubChatIds . length === 0 ) {
1182+ // Early return if nothing to check
1183+ if ( ( ! input . openSubChatIds || input . openSubChatIds . length === 0 ) &&
1184+ ( ! input . chatIds || input . chatIds . length === 0 ) ) {
11791185 return [ ]
11801186 }
11811187
1182- // Query only the specified sub-chats (VS Code style: load only what's needed)
1183- const allChats = db
1184- . select ( {
1185- chatId : subChats . chatId ,
1186- subChatId : subChats . id ,
1187- messages : subChats . messages ,
1188- } )
1189- . from ( subChats )
1190- . where ( inArray ( subChats . id , input . openSubChatIds ) )
1191- . all ( )
1188+ // Query sub-chats based on input mode
1189+ let allChats : Array < { chatId : string | null ; subChatId : string ; messages : string | null } >
1190+
1191+ if ( input . chatIds && input . chatIds . length > 0 ) {
1192+ // Archive mode: query all sub-chats for given chat IDs
1193+ allChats = db
1194+ . select ( {
1195+ chatId : subChats . chatId ,
1196+ subChatId : subChats . id ,
1197+ messages : subChats . messages ,
1198+ } )
1199+ . from ( subChats )
1200+ . where ( inArray ( subChats . chatId , input . chatIds ) )
1201+ . all ( )
1202+ } else {
1203+ // Main sidebar mode: query specific sub-chats
1204+ allChats = db
1205+ . select ( {
1206+ chatId : subChats . chatId ,
1207+ subChatId : subChats . id ,
1208+ messages : subChats . messages ,
1209+ } )
1210+ . from ( subChats )
1211+ . where ( inArray ( subChats . id , input . openSubChatIds ! ) )
1212+ . all ( )
1213+ }
11921214
11931215 // Aggregate stats per workspace (chatId)
11941216 const statsMap = new Map <
@@ -1198,6 +1220,7 @@ export const chatsRouter = router({
11981220
11991221 for ( const row of allChats ) {
12001222 if ( ! row . messages || ! row . chatId ) continue
1223+ const chatId = row . chatId // TypeScript narrowing
12011224
12021225 try {
12031226 const messages = JSON . parse ( row . messages ) as Array < {
@@ -1274,15 +1297,15 @@ export const chatsRouter = router({
12741297 }
12751298
12761299 // Add to workspace total
1277- const existing = statsMap . get ( row . chatId ) || {
1300+ const existing = statsMap . get ( chatId ) || {
12781301 additions : 0 ,
12791302 deletions : 0 ,
12801303 fileCount : 0 ,
12811304 }
12821305 existing . additions += subChatAdditions
12831306 existing . deletions += subChatDeletions
12841307 existing . fileCount += subChatFileCount
1285- statsMap . set ( row . chatId , existing )
1308+ statsMap . set ( chatId , existing )
12861309 } catch {
12871310 // Skip invalid JSON
12881311 }
@@ -1339,32 +1362,34 @@ export const chatsRouter = router({
13391362
13401363 // Traverse messages from end to find unapproved ExitPlanMode
13411364 // Logic matches active-chat.tsx hasUnapprovedPlan
1342- let hasUnapprovedPlan = false
1343-
1344- for ( let i = messages . length - 1 ; i >= 0 ; i -- ) {
1345- const msg = messages [ i ]
1346- if ( ! msg ) continue
1347-
1348- // If user message says "Build plan" or "Implement plan" (exact match), plan is already approved
1349- if ( msg . role === "user" ) {
1350- const textPart = msg . parts ?. find ( ( p ) => p . type === "text" )
1351- const text = textPart ?. text || ""
1352- const normalizedText = text . trim ( ) . toLowerCase ( )
1353- if ( normalizedText === "implement plan" || normalizedText === "build plan" ) {
1354- break // Plan was approved, stop searching
1365+ const checkHasUnapprovedPlan = ( ) : boolean => {
1366+ for ( let i = messages . length - 1 ; i >= 0 ; i -- ) {
1367+ const msg = messages [ i ]
1368+ if ( ! msg ) continue
1369+
1370+ // If user message says "Build plan" or "Implement plan" (exact match), plan is already approved
1371+ if ( msg . role === "user" ) {
1372+ const textPart = msg . parts ?. find ( ( p ) => p . type === "text" )
1373+ const text = textPart ?. text || ""
1374+ const normalizedText = text . trim ( ) . toLowerCase ( )
1375+ if ( normalizedText === "implement plan" || normalizedText === "build plan" ) {
1376+ return false // Plan was approved
1377+ }
13551378 }
1356- }
13571379
1358- // If assistant message with ExitPlanMode, we found an unapproved plan
1359- if ( msg . role === "assistant" && msg . parts ) {
1360- const exitPlanPart = msg . parts . find ( ( p ) => p . type === "tool-ExitPlanMode" )
1361- if ( exitPlanPart ) {
1362- hasUnapprovedPlan = true
1363- break
1380+ // If assistant message with ExitPlanMode that has output.plan , we found an unapproved plan
1381+ if ( msg . role === "assistant" && msg . parts ) {
1382+ const exitPlanPart = msg . parts . find ( ( p ) => p . type === "tool-ExitPlanMode" ) as { output ?: { plan ?: string } } | undefined
1383+ if ( exitPlanPart ?. output ?. plan ) {
1384+ return true
1385+ }
13641386 }
13651387 }
1388+ return false
13661389 }
13671390
1391+ const hasUnapprovedPlan = checkHasUnapprovedPlan ( )
1392+
13681393 if ( hasUnapprovedPlan ) {
13691394 pendingApprovals . push ( {
13701395 subChatId : row . subChatId ,
0 commit comments