Skip to content

Commit 15669fd

Browse files
authored
Merge pull request #1246 from kubet/fix/stream-restore-on-thread-navigation
fix: stream restore on thread navigation
2 parents 6278047 + 6347db1 commit 15669fd

File tree

2 files changed

+34
-31
lines changed

2 files changed

+34
-31
lines changed

frontend/src/app/(dashboard)/projects/[projectId]/thread/[threadId]/page.tsx

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ import { ThreadError, UpgradeDialog, ThreadLayout } from '../_components';
2929
import { useVncPreloader } from '@/hooks/useVncPreloader';
3030
import { useThreadAgent } from '@/hooks/react-query/agents/use-agents';
3131
import { AgentRunLimitDialog } from '@/components/thread/agent-run-limit-dialog';
32+
import { useQueryClient } from '@tanstack/react-query';
33+
import { threadKeys } from '@/hooks/react-query/threads/keys';
3234

3335
export default function ThreadPage({
3436
params,
@@ -42,6 +44,7 @@ export default function ThreadPage({
4244
const { projectId, threadId } = unwrappedParams;
4345
const isMobile = useIsMobile();
4446
const searchParams = useSearchParams();
47+
const queryClient = useQueryClient();
4548

4649
// State
4750
const [newMessage, setNewMessage] = useState('');
@@ -61,7 +64,7 @@ export default function ThreadPage({
6164
runningCount: number;
6265
runningThreadIds: string[];
6366
} | null>(null);
64-
67+
6568

6669
// Refs - simplified for flex-column-reverse
6770
const latestMessageRef = useRef<HTMLDivElement>(null);
@@ -184,14 +187,14 @@ export default function ThreadPage({
184187
} else {
185188
// If this is a user message, replace any optimistic user message with temp ID
186189
if (message.type === 'user') {
187-
const optimisticIndex = prev.findIndex(m =>
188-
m.type === 'user' &&
190+
const optimisticIndex = prev.findIndex(m =>
191+
m.type === 'user' &&
189192
m.message_id?.startsWith('temp-') &&
190193
m.content === message.content
191194
);
192195
if (optimisticIndex !== -1) {
193196
// Replace the optimistic message with the real one
194-
return prev.map((m, index) =>
197+
return prev.map((m, index) =>
195198
index === optimisticIndex ? message : m
196199
);
197200
}
@@ -329,7 +332,7 @@ export default function ThreadPage({
329332
if (error instanceof AgentRunLimitError) {
330333
console.log("Caught AgentRunLimitError:", error.detail);
331334
const { running_thread_ids, running_count } = error.detail;
332-
335+
333336
// Show the dialog with limit information
334337
setAgentLimitData({
335338
runningCount: running_count,
@@ -460,13 +463,13 @@ export default function ThreadPage({
460463
}, [initialPanelOpenAttempted, messages, toolCalls, initialLoadCompleted, setIsSidePanelOpen, setCurrentToolIndex]);
461464

462465
useEffect(() => {
463-
console.log('[STREAM STUFF] Stream effect triggered:', {
464-
agentRunId,
465-
currentHookRunId,
466-
initialLoadCompleted,
467-
userInitiatedRun
466+
console.log('[STREAM STUFF] Stream effect triggered:', {
467+
agentRunId,
468+
currentHookRunId,
469+
initialLoadCompleted,
470+
userInitiatedRun
468471
});
469-
472+
470473
// Start streaming if user initiated a run (don't wait for initialLoadCompleted for first-time users)
471474
if (agentRunId && agentRunId !== currentHookRunId && userInitiatedRun) {
472475
console.log('[STREAM STUFF] User-initiated stream starting for agentRunId:', agentRunId);
@@ -575,12 +578,12 @@ export default function ThreadPage({
575578
useEffect(() => {
576579
const handleScroll = () => {
577580
if (!scrollContainerRef.current) return;
578-
581+
579582
const scrollTop = scrollContainerRef.current.scrollTop;
580583
const scrollHeight = scrollContainerRef.current.scrollHeight;
581584
const clientHeight = scrollContainerRef.current.clientHeight;
582585
const threshold = 100;
583-
586+
584587
// With flex-column-reverse, scrollTop becomes NEGATIVE when scrolling up
585588
// Show button when scrollTop < -threshold (scrolled up enough from bottom)
586589
const shouldShow = scrollTop < -threshold && scrollHeight > clientHeight;
@@ -592,7 +595,7 @@ export default function ThreadPage({
592595
scrollContainer.addEventListener('scroll', handleScroll, { passive: true });
593596
// Check initial state
594597
setTimeout(() => handleScroll(), 100);
595-
598+
596599
return () => {
597600
scrollContainer.removeEventListener('scroll', handleScroll);
598601
};

frontend/src/app/(dashboard)/projects/[projectId]/thread/_hooks/useThreadData.ts

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,14 @@ export function useThreadData(threadId: string, projectId: string): UseThreadDat
4646
const projectQuery = useProjectQuery(projectId);
4747
const agentRunsQuery = useAgentRunsQuery(threadId);
4848

49+
4950
useEffect(() => {
5051
let isMounted = true;
52+
53+
// Reset refs when thread changes
54+
agentRunsCheckedRef.current = false;
55+
messagesLoadedRef.current = false;
56+
initialLoadCompleted.current = false;
5157

5258
async function initializeData() {
5359
if (!initialLoadCompleted.current) setIsLoading(true);
@@ -72,6 +78,8 @@ export function useThreadData(threadId: string, projectId: string): UseThreadDat
7278
}
7379

7480
if (messagesQuery.data && !messagesLoadedRef.current) {
81+
82+
7583
const unifiedMessages = (messagesQuery.data || [])
7684
.filter((msg) => msg.type !== 'status')
7785
.map((msg: ApiMessageType) => ({
@@ -86,7 +94,6 @@ export function useThreadData(threadId: string, projectId: string): UseThreadDat
8694
}));
8795

8896
setMessages(unifiedMessages);
89-
console.log('[PAGE] Loaded Messages (excluding status, keeping browser_state):', unifiedMessages.length);
9097
messagesLoadedRef.current = true;
9198

9299
if (!hasInitiallyScrolled.current) {
@@ -95,26 +102,19 @@ export function useThreadData(threadId: string, projectId: string): UseThreadDat
95102
}
96103

97104
if (agentRunsQuery.data && !agentRunsCheckedRef.current && isMounted) {
98-
console.log('[PAGE] Checking for active agent runs...');
105+
106+
99107
agentRunsCheckedRef.current = true;
100-
101-
// Only check for very recent agent runs (last 30 seconds) to avoid false positives
102-
const thirtySecondsAgo = new Date(Date.now() - 30 * 1000);
103-
const recentActiveRun = agentRunsQuery.data.find((run) => {
104-
const runCreatedAt = new Date(run.started_at || 0);
105-
return run.status === 'running' && runCreatedAt > thirtySecondsAgo;
106-
});
107108

108-
if (recentActiveRun && isMounted) {
109-
console.log('[PAGE] Found recent active run on load:', recentActiveRun.id);
110-
setAgentRunId(recentActiveRun.id);
109+
// Check for any running agents - no time restrictions!
110+
const runningRuns = agentRunsQuery.data.filter(r => r.status === 'running');
111+
if (runningRuns.length > 0) {
112+
const latestRunning = runningRuns[0]; // Use first running agent
113+
setAgentRunId(latestRunning.id);
111114
setAgentStatus('running');
112115
} else {
113-
console.log('[PAGE] No recent active agent runs found');
114-
if (isMounted) {
115-
setAgentStatus('idle');
116-
setAgentRunId(null);
117-
}
116+
setAgentStatus('idle');
117+
setAgentRunId(null);
118118
}
119119
}
120120

0 commit comments

Comments
 (0)