fix(sync): unify live session truth across chat and sidebar

This commit is contained in:
Bohdan Triapitsyn
2026-04-16 19:16:32 +03:00
parent 4c4f24a404
commit ddc1039d1c
15 changed files with 904 additions and 299 deletions
@@ -108,6 +108,24 @@ const getNodeChildSignature = (node: SessionNode): string => {
.join('|');
};
const treeContainsSessionId = (node: SessionNode, sessionId: string | null): boolean => {
if (!sessionId) {
return false;
}
if (node.session.id === sessionId) {
return true;
}
for (const child of node.children) {
if (treeContainsSessionId(child, sessionId)) {
return true;
}
}
return false;
};
const areEqual = (prev: Props, next: Props): boolean => {
const prevSession = prev.node.session;
const nextSession = next.node.session;
@@ -121,7 +139,13 @@ const areEqual = (prev: Props, next: Props): boolean => {
if (prev.groupDirectory !== next.groupDirectory) return false;
if (prev.projectId !== next.projectId) return false;
if (prev.archivedBucket !== next.archivedBucket) return false;
if ((prev.currentSessionId === prevSessionId) !== (next.currentSessionId === nextSessionId)) return false;
if (prev.currentSessionId !== next.currentSessionId) {
const prevActiveInTree = treeContainsSessionId(prev.node, prev.currentSessionId);
const nextActiveInTree = treeContainsSessionId(next.node, next.currentSessionId);
if (prevActiveInTree || nextActiveInTree) {
return false;
}
}
if (prev.pinnedSessionIds.has(prevSessionId) !== next.pinnedSessionIds.has(nextSessionId)) return false;
if (prev.expandedParents.has(prevSessionId) !== next.expandedParents.has(nextSessionId)) return false;
if (prev.hasSessionSearchQuery !== next.hasSessionSearchQuery) return false;
@@ -1,4 +1,5 @@
import type { Session } from '@opencode-ai/sdk/v2';
import type { SessionStatus } from '@opencode-ai/sdk/v2/client';
export const ACTIVE_NOW_STORAGE_KEY = 'oc.sessions.activeNow';
export const ACTIVE_NOW_MAX_AGE_MS = 36 * 60 * 60 * 1000;
@@ -104,4 +105,20 @@ export const deriveActiveNowSessions = (
return sortSessionsByUpdated(sessions);
};
export const deriveLiveActiveNowSessions = (
sessions: Session[],
statuses: Record<string, SessionStatus>,
): Session[] => {
const activeSessions = sessions.filter((session) => {
if (isArchivedSession(session) || isSubtaskSession(session)) {
return false;
}
const status = statuses[session.id];
return status?.type === 'busy' || status?.type === 'retry';
});
return sortSessionsByUpdated(activeSessions);
};
export const getSessionUpdatedAtMs = getSessionUpdatedAt;