mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-04-08 03:00:28 -04:00
fix(copilot): prevent double output from StreamFinish/mark_task_completed race (#SECRT-2021)
Requested by @0ubbe The CoPilot consistently displays every response twice. Root cause is a race condition between StreamFinish delivery and mark_task_completed: 1. Executor publishes StreamFinish to Redis stream 2. Frontend receives it, transitions to 'ready', invalidates cache 3. mark_task_completed runs AFTER StreamFinish (separate operation) 4. Session refetch sees task still 'running' → spurious resumeStream() 5. Resume replays entire Redis stream from '0-0' → double output Backend fix (processor.py): Intercept StreamFinish from the generator instead of publishing it directly. mark_task_completed atomically sets status to 'completed' THEN publishes StreamFinish, ensuring clients never see the finish event while the task is still marked as running. Frontend fix (useCopilotPage.ts): Only reset hasResumedRef on error (SSE drop), not on clean stream finish. This prevents the spurious resume even if the backend timing changes.
This commit is contained in:
@@ -236,7 +236,10 @@ export function useCopilotPage() {
|
||||
}, [hydratedMessages, setMessages, status]);
|
||||
|
||||
// Ref: tracks whether we've already resumed for a given session.
|
||||
// Format: Map<sessionId, hasResumed>
|
||||
// Only cleared on error (SSE drop) to allow re-resume when the backend
|
||||
// task is still running. Not cleared on clean finish (status "ready")
|
||||
// to prevent a spurious resume if the session refetch races with
|
||||
// mark_session_completed (SECRT-2021).
|
||||
const hasResumedRef = useRef<Map<string, boolean>>(new Map());
|
||||
|
||||
// When the stream ends (or drops), invalidate the session cache so the
|
||||
@@ -254,6 +257,13 @@ export function useCopilotPage() {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: getGetV2GetSessionQueryKey(sessionId),
|
||||
});
|
||||
// Only allow re-resume on error (SSE drop without clean finish).
|
||||
// On clean finish (status === "ready"), the backend task is done —
|
||||
// resetting the ref would allow a spurious resume if the session
|
||||
// refetch races with mark_session_completed (SECRT-2021).
|
||||
if (status === "error") {
|
||||
hasResumedRef.current.delete(sessionId);
|
||||
}
|
||||
}
|
||||
}, [status, sessionId, queryClient]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user