@@ -280,6 +280,8 @@ export function ChatPanel({
280280 setVisibleMessages ( [ ] ) ;
281281 return ;
282282 }
283+ // Clear processed message IDs when switching sessions
284+ processedMessageIdsRef . current . clear ( ) ;
283285 // Load history if not yet loaded
284286 fetchSessionHistory ( activeSessionId ) ;
285287 setVisibleMessages ( messagesBySession [ activeSessionId ] ?? [ ] ) ;
@@ -641,9 +643,70 @@ export function ChatPanel({
641643 if ( Object . keys ( modeUpdates ) . length > 0 ) {
642644 setSessionModeById ( ( prev ) => ( { ...prev , ...modeUpdates } ) ) ;
643645 }
644- // eslint-disable-next-line react-hooks/exhaustive-deps
645646 } , [ updates ] ) ;
646647
648+ // ── Extract tasks from messages after SSE updates ────────────────────
649+ // Track which messages have been checked for tasks to avoid re-processing
650+ const processedMessageIdsRef = useRef < Set < string > > ( new Set ( ) ) ;
651+
652+ useEffect ( ( ) => {
653+ if ( ! onTasksDetected || ! activeSessionId ) return ;
654+
655+ const messages = messagesBySession [ activeSessionId ] ;
656+ if ( ! messages || messages . length === 0 ) return ;
657+
658+ // Check if any NEW assistant message contains task blocks
659+ let detectedTasks : ParsedTask [ ] = [ ] ;
660+ let hasNewTasksToExtract = false ;
661+
662+ for ( const msg of messages ) {
663+ if ( msg . role === "assistant" &&
664+ ! processedMessageIdsRef . current . has ( msg . id ) &&
665+ hasTaskBlocks ( msg . content ) ) {
666+ hasNewTasksToExtract = true ;
667+ break ;
668+ }
669+ }
670+
671+ if ( ! hasNewTasksToExtract ) return ;
672+
673+ // Extract tasks and clean message content
674+ setMessagesBySession ( ( prev ) => {
675+ const msgs = prev [ activeSessionId ] ;
676+ if ( ! msgs ) return prev ;
677+
678+ const arr = [ ...msgs ] ;
679+ let tasksFound = false ;
680+
681+ for ( let i = 0 ; i < arr . length ; i ++ ) {
682+ const msg = arr [ i ] ;
683+ if ( msg . role === "assistant" &&
684+ ! processedMessageIdsRef . current . has ( msg . id ) &&
685+ hasTaskBlocks ( msg . content ) ) {
686+ const { tasks, cleanedContent } = extractTaskBlocks ( msg . content ) ;
687+ if ( tasks . length > 0 ) {
688+ // Replace the message content with cleaned version (tasks removed)
689+ arr [ i ] = { ...msg , content : cleanedContent } ;
690+ detectedTasks = tasks ;
691+ tasksFound = true ;
692+ // Mark this message as processed
693+ processedMessageIdsRef . current . add ( msg . id ) ;
694+ }
695+ }
696+ }
697+
698+ if ( tasksFound ) {
699+ return { ...prev , [ activeSessionId ] : arr } ;
700+ }
701+ return prev ;
702+ } ) ;
703+
704+ // Notify parent about detected tasks
705+ if ( detectedTasks . length > 0 ) {
706+ onTasksDetected ( detectedTasks ) ;
707+ }
708+ } , [ messagesBySession , activeSessionId , onTasksDetected ] ) ;
709+
647710 // ── Actions ──────────────────────────────────────────────────────────
648711
649712 const handleRepoChange = onRepoChange ;
@@ -711,42 +774,8 @@ export function ChatPanel({
711774 streamingMsgIdRef . current [ sid ] = null ;
712775 streamingThoughtIdRef . current [ sid ] = null ;
713776
714- // After prompt completes, check assistant messages for @@@task blocks
715- if ( onTasksDetected ) {
716- let detectedTasks : ParsedTask [ ] = [ ] ;
717-
718- setMessagesBySession ( ( prev ) => {
719- const msgs = prev [ sid ] ;
720- if ( ! msgs ) return prev ;
721-
722- const arr = [ ...msgs ] ;
723- let tasksFound = false ;
724-
725- for ( let i = 0 ; i < arr . length ; i ++ ) {
726- const msg = arr [ i ] ;
727- if ( msg . role === "assistant" && hasTaskBlocks ( msg . content ) ) {
728- const { tasks, cleanedContent } = extractTaskBlocks ( msg . content ) ;
729- if ( tasks . length > 0 ) {
730- // Replace the message content with cleaned version (tasks removed)
731- arr [ i ] = { ...msg , content : cleanedContent } ;
732- detectedTasks = tasks ;
733- tasksFound = true ;
734- }
735- }
736- }
737-
738- if ( tasksFound ) {
739- return { ...prev , [ sid ] : arr } ;
740- }
741- return prev ;
742- } ) ;
743-
744- // Call onTasksDetected outside the state updater to avoid setState-during-render
745- if ( detectedTasks . length > 0 ) {
746- onTasksDetected ( detectedTasks ) ;
747- }
748- }
749- } , [ activeSessionId , onEnsureSession , onSelectSession , prompt , repoSelection , onLoadSkill , acp , onTasksDetected ] ) ;
777+ // Task extraction is now handled by the useEffect that watches messagesBySession
778+ } , [ activeSessionId , onEnsureSession , onSelectSession , prompt , repoSelection , onLoadSkill , acp ] ) ;
750779
751780 // ── Render ───────────────────────────────────────────────────────────
752781
0 commit comments