fix chat session lineage visibility (#228)
This commit is contained in:
@@ -205,15 +205,8 @@ function timingMatchesParent(parent: ConversationSessionRow | undefined, child:
|
||||
return Math.abs(Number(child.started_at || 0) - Number(parent.ended_at || 0)) <= LINEAGE_TOLERANCE_SECONDS
|
||||
}
|
||||
|
||||
function isBranchRoot(session: ConversationSessionRow | undefined, byId: Map<string, ConversationSessionRow>): boolean {
|
||||
if (!session?.parent_session_id) return false
|
||||
const parent = byId.get(session.parent_session_id)
|
||||
return !!parent && parent.end_reason === 'branched' && timingMatchesParent(parent, session)
|
||||
}
|
||||
|
||||
function isVisibleRoot(session: ConversationSessionRow | undefined, byId: Map<string, ConversationSessionRow>): boolean {
|
||||
if (!session || session.source === 'tool') return false
|
||||
return session.parent_session_id == null || isBranchRoot(session, byId)
|
||||
function isCompressionEndReason(reason: string | null): boolean {
|
||||
return reason === 'compression' || reason === 'compressed'
|
||||
}
|
||||
|
||||
function continuationCandidates(parent: ConversationSessionRow, byId: Map<string, ConversationSessionRow>, childrenByParent: Map<string | null, string[]>): ConversationSessionRow[] {
|
||||
@@ -233,7 +226,7 @@ function continuationCandidates(parent: ConversationSessionRow, byId: Map<string
|
||||
}
|
||||
|
||||
function nextContinuationChild(parent: ConversationSessionRow, byId: Map<string, ConversationSessionRow>, childrenByParent: Map<string | null, string[]>): ConversationSessionRow | null {
|
||||
if (parent.end_reason !== 'compression') return null
|
||||
if (!isCompressionEndReason(parent.end_reason)) return null
|
||||
const candidates = continuationCandidates(parent, byId, childrenByParent)
|
||||
if (candidates.length === 1) return candidates[0]
|
||||
|
||||
@@ -247,6 +240,33 @@ function nextContinuationChild(parent: ConversationSessionRow, byId: Map<string,
|
||||
return null
|
||||
}
|
||||
|
||||
function isCompressionContinuationChild(session: ConversationSessionRow | undefined, byId: Map<string, ConversationSessionRow>, childrenByParent: Map<string | null, string[]>): boolean {
|
||||
if (!session?.parent_session_id) return false
|
||||
const parent = byId.get(session.parent_session_id)
|
||||
if (!parent) return false
|
||||
return nextContinuationChild(parent, byId, childrenByParent)?.id === session.id
|
||||
}
|
||||
|
||||
function compressionChainRootId(sessionId: string, byId: Map<string, ConversationSessionRow>, childrenByParent: Map<string | null, string[]>): string | null {
|
||||
let current = byId.get(sessionId) || null
|
||||
if (!current || current.source === 'tool') return null
|
||||
|
||||
const seen = new Set<string>()
|
||||
while (current?.parent_session_id && !seen.has(current.id)) {
|
||||
seen.add(current.id)
|
||||
const parent = byId.get(current.parent_session_id)
|
||||
if (!parent) break
|
||||
if (nextContinuationChild(parent, byId, childrenByParent)?.id !== current.id) break
|
||||
current = parent
|
||||
}
|
||||
return current?.id || null
|
||||
}
|
||||
|
||||
function isVisibleConversationStart(session: ConversationSessionRow | undefined, byId: Map<string, ConversationSessionRow>, childrenByParent: Map<string | null, string[]>): boolean {
|
||||
if (!session || session.source === 'tool') return false
|
||||
return !isCompressionContinuationChild(session, byId, childrenByParent)
|
||||
}
|
||||
|
||||
function collectConversationChain(rootId: string, byId: Map<string, ConversationSessionRow>, childrenByParent: Map<string | null, string[]>): ConversationSessionRow[] {
|
||||
const chain: ConversationSessionRow[] = []
|
||||
const seen = new Set<string>()
|
||||
@@ -294,10 +314,10 @@ function aggregateSummary(rootId: string, byId: Map<string, ConversationSessionR
|
||||
const costStatuses = Array.from(new Set(chain.map(session => safeText(session.cost_status)).filter(Boolean)))
|
||||
|
||||
return {
|
||||
...toSummary(root),
|
||||
title: root.title || firstPreview || null,
|
||||
preview: root.preview || firstPreview,
|
||||
model: safeText(last?.model || root.model),
|
||||
...toSummary(last),
|
||||
title: last.title || root.title || firstPreview || null,
|
||||
preview: last.preview || root.preview || firstPreview,
|
||||
started_at: Number(root.started_at || 0),
|
||||
ended_at: last?.ended_at ?? null,
|
||||
last_active: Math.max(...chain.map(session => session.last_active)),
|
||||
is_active: chain.some(session => session.is_active),
|
||||
@@ -427,7 +447,7 @@ export async function listConversationSummariesFromDb(options: ConversationListO
|
||||
}
|
||||
|
||||
const summaries = sessions
|
||||
.filter(session => isVisibleRoot(session, byId))
|
||||
.filter(session => isVisibleConversationStart(session, byId, childrenByParent))
|
||||
.map(session => aggregateSummary(session.id, byId, childrenByParent))
|
||||
.filter((summary): summary is ConversationSummary => !!summary)
|
||||
|
||||
@@ -452,9 +472,12 @@ export async function getConversationDetailFromDb(sessionId: string, options: Co
|
||||
if (!session || session.source === 'tool') return null
|
||||
chain = [session]
|
||||
} else {
|
||||
const root = byId.get(sessionId)
|
||||
if (!isVisibleRoot(root, byId)) return null
|
||||
chain = collectConversationChain(sessionId, byId, childrenByParent)
|
||||
const session = byId.get(sessionId)
|
||||
if (!session || session.source === 'tool') return null
|
||||
const rootId = compressionChainRootId(sessionId, byId, childrenByParent)
|
||||
if (!rootId) return null
|
||||
if (!isVisibleConversationStart(byId.get(rootId), byId, childrenByParent)) return null
|
||||
chain = collectConversationChain(rootId, byId, childrenByParent)
|
||||
}
|
||||
|
||||
if (!chain.length) return null
|
||||
|
||||
Reference in New Issue
Block a user