Fix session status hydration and compaction transitions

This commit is contained in:
Shantur Rathore
2026-01-06 18:03:42 +00:00
parent f24e360d78
commit 315abf21e6
6 changed files with 87 additions and 282 deletions

View File

@@ -21,13 +21,12 @@ import type { PermissionReplyEventPropertiesLike, PermissionRequestLike } from "
import { showToastNotification, ToastVariant } from "../lib/notifications"
import { instances, addPermissionToQueue, removePermissionFromQueue } from "./instances"
import { showAlertDialog } from "./alerts"
import { createClientSession, Session, SessionStatus } from "../types/session"
import { createClientSession, mapSdkSessionStatus, type Session, type SessionStatus } from "../types/session"
import { sessions, setSessions, syncInstanceSessionIndicator, withSession } from "./session-state"
import { normalizeMessagePart } from "./message-v2/normalizers"
import { updateSessionInfo } from "./message-v2/session-info"
import { loadMessages } from "./session-api"
import { setSessionCompactionState } from "./session-compaction"
import {
applyPartUpdateV2,
replaceMessageIdV2,
@@ -56,31 +55,16 @@ interface TuiToastEvent {
const ALLOWED_TOAST_VARIANTS = new Set<ToastVariant>(["info", "success", "warning", "error"])
const mapSdkSessionStatus = (status: EventSessionStatus["properties"]["status"]): SessionStatus => {
if (!status || status.type === "idle") {
return "idle"
}
if (status.type === "retry") {
return "working"
}
return "working"
}
function applySessionStatus(
instanceId: string,
sessionId: string,
status: SessionStatus,
bumpUpdatedOnTransition = false,
) {
function applySessionStatus(instanceId: string, sessionId: string, status: SessionStatus) {
withSession(instanceId, sessionId, (session) => {
const current = session.status ?? "idle"
if (current === status) return false
session.status = status
if (bumpUpdatedOnTransition) {
session.time = { ...(session.time ?? {}), updated: Date.now() }
if (current === "compacting" && status !== "compacting") {
return false
}
session.status = status
})
}
@@ -94,7 +78,17 @@ async function fetchSessionInfo(instanceId: string, sessionId: string): Promise<
"session.get",
)
const fetched = createClientSession(info, instanceId)
let fetchedStatus: SessionStatus = "idle"
try {
const statuses = await requestData<Record<string, any>>(instance.client.session.status(), "session.status")
const rawStatus = (info as any)?.status ?? statuses?.[sessionId]
const hasType = rawStatus && typeof rawStatus === "object" && typeof rawStatus.type === "string"
fetchedStatus = hasType ? mapSdkSessionStatus(rawStatus) : "idle"
} catch (error) {
log.error("Failed to fetch session status", error)
}
const fetched = createClientSession(info, instanceId, "", { providerId: "", modelId: "" }, fetchedStatus)
let updatedInstanceSessions: Map<string, Session> | undefined
@@ -106,7 +100,7 @@ async function fetchSessionInfo(instanceId: string, sessionId: string): Promise<
...fetched,
agent: existing?.agent ?? fetched.agent,
model: existing?.model ?? fetched.model,
status: existing?.status ?? fetched.status,
status: existing?.status === "compacting" ? "compacting" : fetched.status,
pendingPermission: existing?.pendingPermission ?? fetched.pendingPermission,
}
instanceSessions.set(sessionId, merged)
@@ -124,19 +118,14 @@ async function fetchSessionInfo(instanceId: string, sessionId: string): Promise<
}
}
function ensureSessionStatus(
instanceId: string,
sessionId: string,
status: SessionStatus,
bumpUpdatedOnTransition = false,
) {
function ensureSessionStatus(instanceId: string, sessionId: string, status: SessionStatus) {
const instanceSessions = sessions().get(instanceId)
const existing = instanceSessions?.get(sessionId)
if (existing) {
if ((existing.status ?? "idle") === status) {
return
}
applySessionStatus(instanceId, sessionId, status, bumpUpdatedOnTransition)
applySessionStatus(instanceId, sessionId, status)
return
}
@@ -148,7 +137,7 @@ function ensureSessionStatus(
const pending = (async () => {
const fetched = await fetchSessionInfo(instanceId, sessionId)
if (!fetched) return
applySessionStatus(instanceId, sessionId, status, bumpUpdatedOnTransition)
applySessionStatus(instanceId, sessionId, status)
})()
pendingSessionFetches.set(key, pending)
@@ -193,17 +182,10 @@ function handleMessageUpdate(instanceId: string, event: MessageUpdateEvent | Mes
const sessionId = typeof part.sessionID === "string" ? part.sessionID : fallbackSessionId
const messageId = typeof part.messageID === "string" ? part.messageID : fallbackMessageId
if (!sessionId || !messageId) return
const session = instanceSessions?.get(sessionId)
if (!session) {
ensureSessionStatus(instanceId, sessionId, "working", true)
return
if (part.type === "compaction") {
ensureSessionStatus(instanceId, sessionId, "compacting")
}
if (session.status !== "working" && session.status !== "compacting") {
applySessionStatus(instanceId, sessionId, "working", true)
}
const store = messageStoreBus.getOrCreate(instanceId)
const role: MessageRole = resolveMessageRole(messageInfo)
const createdAt = typeof messageInfo?.time?.created === "number" ? messageInfo.time.created : Date.now()
@@ -246,15 +228,9 @@ function handleMessageUpdate(instanceId: string, event: MessageUpdateEvent | Mes
const messageId = typeof info.id === "string" ? info.id : undefined
if (!sessionId || !messageId) return
const session = instanceSessions?.get(sessionId)
if (!session) {
ensureSessionStatus(instanceId, sessionId, "working", true)
return
}
if (session.status !== "working" && session.status !== "compacting") {
applySessionStatus(instanceId, sessionId, "working", true)
}
withSession(instanceId, sessionId, (session) => {
session.time = { ...(session.time ?? {}), updated: Date.now() }
})
const store = messageStoreBus.getOrCreate(instanceId)
@@ -295,10 +271,6 @@ function handleSessionUpdate(instanceId: string, event: EventSessionUpdated): vo
if (!info) return
const compactingFlag = info.time?.compacting
const isCompacting = typeof compactingFlag === "number" ? compactingFlag > 0 : Boolean(compactingFlag)
setSessionCompactionState(instanceId, info.id, isCompacting)
const instanceSessions = sessions().get(instanceId) ?? new Map<string, Session>()
const existingSession = instanceSessions.get(info.id)
@@ -314,7 +286,7 @@ function handleSessionUpdate(instanceId: string, event: EventSessionUpdated): vo
providerId: "",
modelId: "",
},
status: isCompacting ? "compacting" : "idle",
status: "idle",
version: info.version || "0",
time: info.time
? { ...info.time }
@@ -344,14 +316,10 @@ function handleSessionUpdate(instanceId: string, event: EventSessionUpdated): vo
...existingSession.time,
...(info.time ?? {}),
}
if (!info.time?.updated) {
mergedTime.updated = Date.now()
}
const updatedSession = {
...existingSession,
title: info.title || existingSession.title,
status: isCompacting ? "compacting" : (existingSession.status ?? "idle"),
status: existingSession.status ?? "idle",
time: mergedTime,
revert: info.revert
? {
@@ -383,7 +351,7 @@ function handleSessionIdle(instanceId: string, event: EventSessionIdle): void {
const sessionId = event.properties?.sessionID
if (!sessionId) return
ensureSessionStatus(instanceId, sessionId, "idle", true)
ensureSessionStatus(instanceId, sessionId, "idle")
log.info(`[SSE] Session idle: ${sessionId}`)
}
@@ -392,7 +360,7 @@ function handleSessionStatus(instanceId: string, event: EventSessionStatus): voi
if (!sessionId) return
const status = mapSdkSessionStatus(event.properties.status)
ensureSessionStatus(instanceId, sessionId, status, true)
ensureSessionStatus(instanceId, sessionId, status)
log.info(`[SSE] Session status updated: ${sessionId}`, { status })
}
@@ -402,14 +370,14 @@ function handleSessionCompacted(instanceId: string, event: EventSessionCompacted
log.info(`[SSE] Session compacted: ${sessionID}`)
setSessionCompactionState(instanceId, sessionID, false)
ensureSessionStatus(instanceId, sessionID, "idle", true)
withSession(instanceId, sessionID, (session) => {
const time = { ...(session.time ?? {}) }
time.compacting = 0
session.time = time
})
const existing = sessions().get(instanceId)?.get(sessionID)
if (existing) {
withSession(instanceId, sessionID, (session) => {
session.status = "working"
})
} else {
ensureSessionStatus(instanceId, sessionID, "working")
}
loadMessages(instanceId, sessionID, true).catch((error) => log.error("Failed to reload session after compaction", error))