Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/store/chatStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1762,6 +1762,15 @@ const chatStore = (initial?: Partial<ChatStore>) =>

// Create AbortController for this task's SSE connection
// First check if there's already an active SSE connection for this task
if (activeSSEControllers[newTaskId] && type === 'replay') {
// A history replay must never tear down a live run's stream: the
// ongoing run is the fresher state, and aborting it kills the run
// on the backend. Leave the live connection alone.
console.warn(
`Task ${newTaskId} already has an active SSE connection, skipping history replay`
);
return;
}
if (activeSSEControllers[newTaskId]) {
console.warn(
`Task ${newTaskId} already has an active SSE connection, aborting old one`
Expand Down
18 changes: 17 additions & 1 deletion src/store/projectStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ import {
} from '@/types/constants';
import { create } from 'zustand';
import { getAuthStore } from './authStore';
import { createChatStoreInstance, VanillaChatStore } from './chatStore';
import {
createChatStoreInstance,
hasActiveSSEConnection,
VanillaChatStore,
} from './chatStore';
import {
projectMetaFromServer,
useSpaceStore,
Expand Down Expand Up @@ -1042,6 +1046,18 @@ const projectStore = create<ProjectStore>()((set, get) => ({
) {
return;
}
// Never evict a project that still has a live run. Eviction drops the
// runtime chat stores, so returning to the project rebuilds it from
// history and replays the ongoing task id -- which aborts the live
// run's stream and kills the run on the backend. Keep the stale flag
// so the eviction simply happens on a later, safe transition.
const outgoingProject = get().projects[previousProjectId];
const outgoingTaskIds = Object.values(
outgoingProject?.chatStores ?? {}
).flatMap((chatStore) => Object.keys(chatStore.getState().tasks));
if (hasActiveSSEConnection(outgoingTaskIds)) {
return;
}
// _evictProjectRuntime handles staleProjectIds cleanup itself.
get()._evictProjectRuntime(previousProjectId);
},
Expand Down