Skip to content

Commit 91db351

Browse files
committed
fix: scheduled task storage and indexeddb optimization
- Add scheduledTaskId field to task creation for proper linking - Implement debounce mechanism (300ms) for IndexedDB saves during task execution - Save immediately when task ends (done/error/abort status) - Fix detail panel reset when continuing conversation from history - Bump version to 0.0.11
1 parent 21e040f commit 91db351

File tree

4 files changed

+119
-58
lines changed

4 files changed

+119
-58
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ai-browser",
3-
"version": "0.0.10",
3+
"version": "0.0.11",
44
"description": "DeepFundAI Browser - AI-Powered Intelligent Browser",
55
"author": "Shuai Liu <lsustc@mail.ustc.edu.cn>",
66
"private": true,

src/hooks/useMessageHandlers.ts

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,16 @@ import { detectFileType } from '@/utils/fileDetection';
88

99
interface UseMessageHandlersOptions {
1010
isHistoryMode: boolean;
11+
isTaskDetailMode: boolean;
12+
scheduledTaskId?: string;
1113
taskIdRef: React.RefObject<string>;
1214
messageProcessorRef: React.RefObject<MessageProcessor>;
1315
currentTaskId: string;
1416
tasks: Task[];
1517
showDetailAgents: string[];
1618
toolHistory: any[];
1719
updateTask: (taskId: string, updates: Partial<Task>) => void;
20+
createTask: (taskId: string, initialData: Partial<Task>) => void;
1821
replaceTaskId: (oldTaskId: string, newTaskId: string) => void;
1922
setCurrentTaskId: (taskId: string) => void;
2023
setCurrentTool: (tool: { toolName: string; operation: string; status: 'running' | 'completed' | 'error' } | null) => void;
@@ -28,13 +31,16 @@ interface UseMessageHandlersOptions {
2831
*/
2932
export const useMessageHandlers = ({
3033
isHistoryMode,
34+
isTaskDetailMode,
35+
scheduledTaskId,
3136
taskIdRef,
3237
messageProcessorRef,
3338
currentTaskId,
3439
tasks,
3540
showDetailAgents,
3641
toolHistory,
3742
updateTask,
43+
createTask,
3844
replaceTaskId,
3945
setCurrentTaskId,
4046
setCurrentTool,
@@ -175,22 +181,44 @@ export const useMessageHandlers = ({
175181
if (message.taskId && !currentTaskId && !message.taskId.startsWith('temp-')) {
176182
setCurrentTaskId(message.taskId);
177183
}
184+
178185
const taskIdToUpdate = message.taskId || taskIdRef.current;
179186
if (taskIdToUpdate) {
180-
const updates: Partial<Task> = {
181-
messages: updatedMessages
182-
};
187+
const existingTask = tasks.find(task => task.id === taskIdToUpdate);
183188

184-
if (message.type === 'workflow' && message.workflow?.name) {
185-
updates.name = message.workflow.name;
186-
updates.workflow = message.workflow;
187-
}
189+
if (existingTask) {
190+
const updates: Partial<Task> = {
191+
messages: updatedMessages
192+
};
188193

189-
if (message.type === 'error') {
190-
updates.status = 'error';
191-
}
194+
if (message.type === 'workflow' && message.workflow?.name) {
195+
updates.name = message.workflow.name;
196+
updates.workflow = message.workflow;
197+
}
198+
199+
if (message.type === 'error') {
200+
updates.status = 'error';
201+
}
192202

193-
updateTask(taskIdToUpdate, updates);
203+
updateTask(taskIdToUpdate, updates);
204+
} else {
205+
// Task doesn't exist, create it
206+
const initialData: Partial<Task> = {
207+
name: (message.type === 'workflow' && message.workflow?.name)
208+
? message.workflow.name
209+
: `Task ${taskIdToUpdate.slice(0, 8)}`,
210+
workflow: (message.type === 'workflow' && message.workflow) ? message.workflow : undefined,
211+
messages: updatedMessages,
212+
status: 'running',
213+
taskType: isTaskDetailMode ? 'scheduled' : 'normal',
214+
scheduledTaskId: isTaskDetailMode ? scheduledTaskId : undefined,
215+
startTime: new Date(),
216+
};
217+
218+
taskIdRef.current = taskIdToUpdate;
219+
setCurrentTaskId(taskIdToUpdate);
220+
createTask(taskIdToUpdate, initialData);
221+
}
194222
}
195223

196224
if (message.type.includes('tool')) {

src/hooks/useTaskManager.ts

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState, useCallback } from 'react';
1+
import { useState, useCallback, useRef } from 'react';
22
import { Task, DisplayMessage } from '@/models';
33
import { taskStorage } from '@/services/task-storage';
44

@@ -30,14 +30,39 @@ export const useTaskManager = (): UseTaskManagerReturn => {
3030
const [currentTaskId, setCurrentTaskId] = useState<string>('');
3131
const [isHistoryMode, setIsHistoryMode] = useState<boolean>(false);
3232

33+
// Debounce timers for each task
34+
const saveTaskTimers = useRef<Map<string, NodeJS.Timeout>>(new Map());
35+
3336
// Computed properties
3437
const currentTask = tasks.find(task => task.id === currentTaskId);
3538
const messages = currentTask?.messages || [];
3639

37-
// Automatically save tasks to IndexedDB
38-
const saveTask = useCallback(async (task: Task) => {
40+
// Save task with debounce optimization
41+
const saveTask = useCallback(async (task: Task, immediate = false) => {
3942
try {
40-
await taskStorage.saveTask(task);
43+
if (immediate) {
44+
// Immediate save (when task ends)
45+
const timer = saveTaskTimers.current.get(task.id);
46+
if (timer) {
47+
clearTimeout(timer);
48+
saveTaskTimers.current.delete(task.id);
49+
}
50+
await taskStorage.saveTask(task);
51+
return;
52+
}
53+
54+
// Debounced save (during task execution)
55+
const existingTimer = saveTaskTimers.current.get(task.id);
56+
if (existingTimer) {
57+
clearTimeout(existingTimer);
58+
}
59+
60+
const timer = setTimeout(async () => {
61+
await taskStorage.saveTask(task);
62+
saveTaskTimers.current.delete(task.id);
63+
}, 300); // 300ms debounce
64+
65+
saveTaskTimers.current.set(task.id, timer);
4166
} catch (error) {
4267
console.error('Failed to save task:', error);
4368
}
@@ -60,8 +85,11 @@ export const useTaskManager = (): UseTaskManagerReturn => {
6085
};
6186
updatedTasks[existingTaskIndex] = updatedTask;
6287

63-
// Asynchronous save
64-
saveTask(updatedTask);
88+
// Check if task has ended (status changed to done/error/abort)
89+
const isTaskEnded = updates.status && ['done', 'error', 'abort'].includes(updates.status);
90+
91+
// Save immediately if task ended, otherwise use debounce
92+
saveTask(updatedTask, isTaskEnded);
6593

6694
return updatedTasks;
6795
}

src/pages/main.tsx

Lines changed: 45 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -151,13 +151,16 @@ export default function main() {
151151
getToolStatus,
152152
} = useMessageHandlers({
153153
isHistoryMode,
154+
isTaskDetailMode,
155+
scheduledTaskId: scheduledTaskIdFromUrl,
154156
taskIdRef,
155157
messageProcessorRef,
156158
currentTaskId,
157159
tasks,
158160
showDetailAgents,
159161
toolHistory,
160162
updateTask,
163+
createTask,
161164
replaceTaskId,
162165
setCurrentTaskId,
163166
setCurrentTool,
@@ -219,57 +222,59 @@ export default function main() {
219222
return;
220223
}
221224

222-
try {
223-
// Stop playback before continuing conversation
224-
playback.stop();
225+
// Stop playback before continuing conversation
226+
playback.stop();
225227

226-
// Check if task has workflow
227-
if (!currentTask.workflow) {
228-
antdMessage.error(t('task_missing_context'));
229-
console.error('Task missing workflow:', currentTask);
230-
return;
231-
}
228+
// Check if task has workflow
229+
if (!currentTask.workflow) {
230+
antdMessage.error(t('task_missing_context'));
231+
console.error('Task missing workflow:', currentTask);
232+
return;
233+
}
232234

233-
// Restore task context
234-
const result = await (window.api as any).ekoRestoreTask(
235-
currentTask.workflow,
236-
currentTask.contextParams || {},
237-
currentTask.chainPlanRequest,
238-
currentTask.chainPlanResult
239-
);
235+
// Restore task context
236+
const result = await (window.api as any).ekoRestoreTask(
237+
currentTask.workflow,
238+
currentTask.contextParams || {},
239+
currentTask.chainPlanRequest,
240+
currentTask.chainPlanResult
241+
);
240242

241-
if (!result || !result.success) {
242-
throw new Error('Failed to restore task context');
243-
}
243+
if (!result?.success) {
244+
antdMessage.error(t('continue_conversation_failed'));
245+
return;
246+
}
244247

245-
// Call base handler (exits history mode, generates execution ID)
246-
await handleContinueConversationBase(currentTask);
248+
// Call base handler (exits history mode, generates execution ID)
249+
await handleContinueConversationBase(currentTask);
247250

248-
// Update UI states
249-
setIsViewingAttachment(false);
250-
setCurrentTaskId(currentTask.id);
251-
taskIdRef.current = currentTask.id;
251+
// Update UI states
252+
setIsViewingAttachment(false);
253+
setCurrentTaskId(currentTask.id);
254+
taskIdRef.current = currentTask.id;
252255

253-
// Restore lastUrl if available (but don't auto-expand detail panel)
254-
if (currentTask.lastUrl) {
255-
setCurrentUrl(currentTask.lastUrl);
256-
}
256+
// Reset detail panel: hide playback screenshot
257+
setCurrentHistoryIndex(-1);
258+
await (window.api as any).hideHistoryView?.();
257259

258-
// Restore tool history
259-
setToolHistory(currentTask.toolHistory || []);
260+
// Restore lastUrl and navigate detail view to initial address
261+
if (currentTask.lastUrl) {
262+
setCurrentUrl(currentTask.lastUrl);
263+
await (window.api as any).setDetailViewVisible?.(true);
264+
await (window.api as any).navigateDetailView?.(currentTask.lastUrl);
265+
}
260266

261-
// Restore historical messages to MessageProcessor
262-
if (currentTask.messages && currentTask.messages.length > 0) {
263-
messageProcessorRef.current.setMessages(currentTask.messages);
264-
}
267+
// Restore tool history
268+
setToolHistory(currentTask.toolHistory || []);
265269

266-
antdMessage.success(t('conversation_continued'));
267-
} catch (error) {
268-
console.error('Failed to continue conversation:', error);
269-
antdMessage.error(t('continue_conversation_failed'));
270+
// Restore historical messages to MessageProcessor
271+
if (currentTask.messages && currentTask.messages.length > 0) {
272+
messageProcessorRef.current.setMessages(currentTask.messages);
270273
}
274+
275+
antdMessage.success(t('conversation_continued'));
271276
}, [currentTask, playback, handleContinueConversationBase, antdMessage, t, setIsViewingAttachment,
272-
setCurrentTaskId, taskIdRef, setCurrentUrl, setShowDetail, setToolHistory, messageProcessorRef]);
277+
setCurrentTaskId, taskIdRef, setCurrentUrl, setShowDetail, setToolHistory, setCurrentHistoryIndex, messageProcessorRef]);
273278

274279
// Synchronize taskIdRef
275280
useEffect(() => {

0 commit comments

Comments
 (0)