fix: complete session persistence overhaul (Codex 5.2)
Some checks failed
Release Binaries / release (push) Has been cancelled
Some checks failed
Release Binaries / release (push) Has been cancelled
1. Implemented auto-selection of tasks in MultiXV2 to prevent empty initial state. 2. Added force-loading logic for task session messages with debouncing. 3. Updated session-actions to return full assistant text and immediately persist native messages. 4. Fixed caching logic in instance-shell2 to retain active task sessions in memory.
This commit is contained in:
@@ -77,6 +77,8 @@ export default function MultiXV2(props: MultiXV2Props) {
|
||||
const [soloState, setSoloState] = createSignal({ isApex: false, isAutonomous: false, autoApproval: false, activeTaskId: null as string | null });
|
||||
const [lastAssistantIndex, setLastAssistantIndex] = createSignal(-1);
|
||||
const [bottomSentinel, setBottomSentinel] = createSignal<HTMLDivElement | null>(null);
|
||||
const [hasUserSelection, setHasUserSelection] = createSignal(false);
|
||||
const forcedLoadTimestamps = new Map<string, number>();
|
||||
|
||||
// Helper to check if CURRENT task is sending
|
||||
const isSending = () => {
|
||||
@@ -140,6 +142,10 @@ export default function MultiXV2(props: MultiXV2Props) {
|
||||
setVisibleTasks(allTasks.filter(t => !t.archived));
|
||||
// NOTE: Don't overwrite selectedTaskId from store - local state is authoritative
|
||||
// This prevents the reactive cascade when the store updates
|
||||
if (!selectedTaskId() && !hasUserSelection() && allTasks.length > 0) {
|
||||
const preferredId = session.activeTaskId || allTasks[0].id;
|
||||
setSelectedTaskIdLocal(preferredId);
|
||||
}
|
||||
}
|
||||
|
||||
// Get message IDs for currently selected task
|
||||
@@ -149,6 +155,20 @@ export default function MultiXV2(props: MultiXV2Props) {
|
||||
if (task) {
|
||||
const store = getMessageStore();
|
||||
if (task.taskSessionId) {
|
||||
const cachedIds = store.getSessionMessageIds(task.taskSessionId);
|
||||
if (cachedIds.length === 0) {
|
||||
const lastForced = forcedLoadTimestamps.get(task.taskSessionId) ?? 0;
|
||||
if (Date.now() - lastForced > 1000) {
|
||||
forcedLoadTimestamps.set(task.taskSessionId, Date.now());
|
||||
loadMessages(props.instanceId, task.taskSessionId, true).catch((error) =>
|
||||
log.error("Failed to load task session messages", error)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
loadMessages(props.instanceId, task.taskSessionId).catch((error) =>
|
||||
log.error("Failed to load task session messages", error)
|
||||
);
|
||||
}
|
||||
setMessageIds(store.getSessionMessageIds(task.taskSessionId));
|
||||
} else {
|
||||
setMessageIds(task.messageIds || []);
|
||||
@@ -251,6 +271,7 @@ export default function MultiXV2(props: MultiXV2Props) {
|
||||
const setSelectedTaskId = (id: string | null) => {
|
||||
// Update local state immediately (fast)
|
||||
setSelectedTaskIdLocal(id);
|
||||
setHasUserSelection(true);
|
||||
|
||||
// Immediately sync to load the new task's agent/model
|
||||
syncFromStore();
|
||||
@@ -304,7 +325,7 @@ export default function MultiXV2(props: MultiXV2Props) {
|
||||
syncFromStore();
|
||||
|
||||
// Set the selected task
|
||||
setSelectedTaskIdLocal(taskId);
|
||||
setSelectedTaskId(taskId);
|
||||
|
||||
const s = soloState();
|
||||
if (s.isAutonomous) {
|
||||
@@ -357,7 +378,7 @@ export default function MultiXV2(props: MultiXV2Props) {
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
const result = await addTask(props.instanceId, props.sessionId, title);
|
||||
setSelectedTaskIdLocal(result.id);
|
||||
setSelectedTaskId(result.id);
|
||||
setTimeout(() => syncFromStore(), 50);
|
||||
} catch (error) {
|
||||
log.error("handleCreateTask failed", error);
|
||||
|
||||
@@ -683,7 +683,25 @@ Now analyze the project and report your findings.`
|
||||
})
|
||||
|
||||
const handleSessionSelect = (sessionId: string) => {
|
||||
setActiveSession(props.instance.id, sessionId)
|
||||
if (sessionId === "info") {
|
||||
setActiveSession(props.instance.id, sessionId)
|
||||
return
|
||||
}
|
||||
|
||||
const instanceSessions = sessions().get(props.instance.id)
|
||||
const session = instanceSessions?.get(sessionId)
|
||||
|
||||
if (session?.parentId) {
|
||||
setActiveParentSession(props.instance.id, session.parentId)
|
||||
const parentSession = instanceSessions?.get(session.parentId)
|
||||
const matchingTask = parentSession?.tasks?.find((task) => task.taskSessionId === sessionId)
|
||||
if (matchingTask) {
|
||||
setActiveTask(props.instance.id, session.parentId, matchingTask.id)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
setActiveParentSession(props.instance.id, sessionId)
|
||||
}
|
||||
|
||||
|
||||
@@ -731,6 +749,7 @@ Now analyze the project and report your findings.`
|
||||
const sessionsMap = activeSessions()
|
||||
const parentId = parentSessionIdForInstance()
|
||||
const activeId = activeSessionIdForInstance()
|
||||
const instanceSessions = sessions().get(props.instance.id)
|
||||
setCachedSessionIds((current) => {
|
||||
const next: string[] = []
|
||||
const append = (id: string | null) => {
|
||||
@@ -743,6 +762,16 @@ Now analyze the project and report your findings.`
|
||||
append(parentId)
|
||||
append(activeId)
|
||||
|
||||
const parentSessionId = parentId || activeId
|
||||
const parentSession = parentSessionId ? instanceSessions?.get(parentSessionId) : undefined
|
||||
const activeTaskId = parentSession?.activeTaskId
|
||||
if (activeTaskId && parentSession?.tasks?.length) {
|
||||
const activeTask = parentSession.tasks.find((task) => task.id === activeTaskId)
|
||||
if (activeTask?.taskSessionId) {
|
||||
append(activeTask.taskSessionId)
|
||||
}
|
||||
}
|
||||
|
||||
const limit = parentId ? SESSION_CACHE_LIMIT + 1 : SESSION_CACHE_LIMIT
|
||||
const trimmed = next.length > limit ? next.slice(0, limit) : next
|
||||
const trimmedSet = new Set(trimmed)
|
||||
|
||||
Reference in New Issue
Block a user