Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,27 +1,50 @@
import type { Session } from '@/flow_chat/types/flow-chat';
import { resolveSessionRelationship } from '@/flow_chat/utils/sessionMetadata';
import { sessionBelongsToWorkspaceNavRow } from '@/flow_chat/utils/sessionOrdering';
import { sessionOwningWorkspaceId } from '@/flow_chat/utils/sessionOrdering';

/** Fast parent-id resolver: avoid full relationship normalization for ordinary normal sessions. */
function resolveParentSessionId(session: Session): string | undefined {
if (session.sessionKind === 'normal' && !session.parentSessionId && !session.btwOrigin) {
return undefined;
}
return resolveSessionRelationship(session).parentSessionId;
}

/** Build the same workspace-owned hierarchy for both rows and their count. */
export function getWorkspaceSessionHierarchy(
sessions: Iterable<Session>,
workspaceIds: readonly string[],
): { topLevelSessions: Session[]; childrenByParent: Map<string, Session[]> } {
const scoped = Array.from(sessions).filter(session => {
if (session.isTransient || session.sessionKind === 'subagent') return false;
return workspaceIds.length
? workspaceIds.some(workspaceId => sessionBelongsToWorkspaceNavRow(session, workspaceId))
: !session.workspaceId;
});
const knownIds = new Set(scoped.map(session => session.sessionId));
const hasWorkspaceScope = workspaceIds.length > 0;
const workspaceIdSet = hasWorkspaceScope ? new Set(workspaceIds) : null;
const scoped: Session[] = [];
const knownIds = new Set<string>();

for (const session of sessions) {
if (session.isTransient || session.sessionKind === 'subagent') continue;
const ownerId = sessionOwningWorkspaceId(session);
const inScope = hasWorkspaceScope
? (ownerId ? workspaceIdSet!.has(ownerId) : false)
: !ownerId;
if (!inScope) continue;
scoped.push(session);
knownIds.add(session.sessionId);
}

const topLevelSessions: Session[] = [];
const childrenByParent = new Map<string, Session[]>();
for (const session of scoped) {
const parentId = resolveSessionRelationship(session).parentSessionId;
// Resolve each parent link once, then bucket children and top-level rows in a
// single pass without intermediate arrays.
for (let i = 0; i < scoped.length; i++) {
const session = scoped[i];
const parentId = resolveParentSessionId(session);
if (parentId && knownIds.has(parentId)) {
const children = childrenByParent.get(parentId) ?? [];
children.push(session);
childrenByParent.set(parentId, children);
const children = childrenByParent.get(parentId);
if (children) {
children.push(session);
} else {
childrenByParent.set(parentId, [session]);
}
} else {
topLevelSessions.push(session);
}
Expand Down
Loading