Refactor loop detection logic in ChatInput

- Use message finish state instead of session status for loop detection
- Add lastProcessedMessageId to prevent duplicate loop triggers
- Add XCOpenCodeWeb.exe to .gitignore

Note: Single-file exe now builds to ~150MB (down from ~300MB with Electron)
This commit is contained in:
2026-03-14 19:40:55 +08:00
parent 78937e0679
commit 382b5afdb9
2 changed files with 36 additions and 27 deletions

1
.gitignore vendored
View File

@@ -54,3 +54,4 @@ packages/intellij/
# OS
Thumbs.db
web/XCOpenCodeWeb.exe

View File

@@ -167,11 +167,9 @@ export const ChatInput: React.FC<ChatInputProps> = ({ 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<ChatInputProps> = ({ onOpenSettings, scrollToBo
current: 1,
total: loopNum,
prompt: loopArgs,
lastProcessedMessageId: null,
});
toast.info(`Loop started: ${loopNum} iterations`);
// Clear input
@@ -952,19 +951,30 @@ export const ChatInput: React.FC<ChatInputProps> = ({ 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) {
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 });
setLoopState({
...loopState,
current: next,
lastProcessedMessageId: messageId,
});
useSessionStore.getState().sendMessage(
loopState.prompt,
currentProviderId,
@@ -977,12 +987,10 @@ export const ChatInput: React.FC<ChatInputProps> = ({ onOpenSettings, scrollToBo
inputMode
);
} else {
// Loop complete - all iterations sent
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(() => {