diff --git a/.gitignore b/.gitignore index acbcf0f..cf16a04 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,4 @@ packages/intellij/ # OS Thumbs.db +web/XCOpenCodeWeb.exe diff --git a/ui/src/components/chat/ChatInput.tsx b/ui/src/components/chat/ChatInput.tsx index 31ea32c..240f7b7 100644 --- a/ui/src/components/chat/ChatInput.tsx +++ b/ui/src/components/chat/ChatInput.tsx @@ -167,11 +167,9 @@ export const ChatInput: React.FC = ({ onOpenSettings, scrollToBo current: number; total: number; prompt: string; + lastProcessedMessageId: string | null; } | null>(null); - // Get session status from store for loop detection - const sessionStatus = useSessionStore((state) => state.sessionStatus); - const isDesktopExpanded = isExpandedInput && !isMobile; const sendableAttachedFiles = React.useMemo( @@ -856,6 +854,7 @@ export const ChatInput: React.FC = ({ onOpenSettings, scrollToBo current: 1, total: loopNum, prompt: loopArgs, + lastProcessedMessageId: null, }); toast.info(`Loop started: ${loopNum} iterations`); // Clear input @@ -952,37 +951,46 @@ export const ChatInput: React.FC = ({ onOpenSettings, scrollToBo // Update ref with latest handleSubmit on every render handleSubmitRef.current = handleSubmit; - // Handle loop: detect when AI finishes and send next iteration + // Handle loop: detect when AI finishes (finish === 'stop') and send next iteration React.useEffect(() => { if (!loopState?.active || !currentSessionId) { return; } - const status = sessionStatus?.get(currentSessionId); - if (status?.type === 'idle') { - // AI finished, check if we should continue looping - if (loopState.current <= loopState.total) { - if (loopState.current < loopState.total) { - const next = loopState.current + 1; - setLoopState({ ...loopState, current: next }); - useSessionStore.getState().sendMessage( - loopState.prompt, - currentProviderId, - currentModelId, - currentAgentName, - [], - undefined, - undefined, - currentVariant, - inputMode - ); - } else { - // Loop complete - all iterations sent - setLoopState(null); - } + const messages = useMessageStore.getState().messages.get(currentSessionId); + if (!messages) return; + + const assistantMessages = messages.filter((m) => m.info.role === 'assistant'); + const lastAssistant = assistantMessages[assistantMessages.length - 1]; + if (!lastAssistant) return; + + const messageId = lastAssistant.info.id; + const finish = lastAssistant.info.finish; + + if (finish === 'stop' && messageId !== loopState.lastProcessedMessageId) { + if (loopState.current < loopState.total) { + const next = loopState.current + 1; + setLoopState({ + ...loopState, + current: next, + lastProcessedMessageId: messageId, + }); + useSessionStore.getState().sendMessage( + loopState.prompt, + currentProviderId, + currentModelId, + currentAgentName, + [], + undefined, + undefined, + currentVariant, + inputMode + ); + } else { + setLoopState(null); } } - }, [loopState, sessionStatus, currentSessionId, currentProviderId, currentModelId, currentAgentName, currentVariant, inputMode]); + }, [loopState, sessionMessages, currentSessionId, currentProviderId, currentModelId, currentAgentName, currentVariant, inputMode]); // Primary action for send button - respects queue mode setting const handlePrimaryAction = React.useCallback(() => {