fix: complete session persistence overhaul (Codex 5.2)
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:
Gemini AI
2025-12-27 20:36:43 +04:00
Unverified
parent 5022a23aeb
commit 1e991d9ebd
8 changed files with 235 additions and 20 deletions

View File

@@ -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);