diff --git a/packages/ui/src/stores/session-api.ts b/packages/ui/src/stores/session-api.ts index 4d00cee8..556d625c 100644 --- a/packages/ui/src/stores/session-api.ts +++ b/packages/ui/src/stores/session-api.ts @@ -8,9 +8,11 @@ import { activeSessionId, agents, clearSessionDraftPrompt, + getChildSessions, + isBlankSession, messagesLoaded, - providers, pruneDraftPrompts, + providers, setActiveSessionId, setAgents, setMessagesLoaded, @@ -228,6 +230,10 @@ async function createSession(instanceId: string, agent?: string): Promise { + const instanceSessions = sessions().get(instanceId) + if (!instanceSessions) return + + const deletionPromises: Promise[] = [] + + for (const [sessionId, session] of instanceSessions) { + if (sessionId === excludeSessionId) continue + if (!isBlankSession(session, instanceId)) continue + + deletionPromises.push(deleteSession(instanceId, sessionId).catch((error) => { + console.error(`Failed to delete blank session ${sessionId}:`, error) + })) + } + + if (deletionPromises.length > 0) { + console.log(`Cleaning up ${deletionPromises.length} blank sessions`) + await Promise.all(deletionPromises) + } +} + async function fetchAgents(instanceId: string): Promise { const instance = instances().get(instanceId) if (!instance || !instance.client) { diff --git a/packages/ui/src/stores/session-state.ts b/packages/ui/src/stores/session-state.ts index e646cd64..3fbb9d28 100644 --- a/packages/ui/src/stores/session-state.ts +++ b/packages/ui/src/stores/session-state.ts @@ -221,6 +221,49 @@ function getSessionInfo(instanceId: string, sessionId: string): SessionInfo | un return sessionInfoByInstance().get(instanceId)?.get(sessionId) } +function isBlankSession(session: Session, instanceId: string): boolean { + if (session.parentId === null) { + // Parent session is only blank if actually blank AND has no children + + const sessionInfo = getSessionInfo(instanceId, session.id) + const hasChildren = getChildSessions(instanceId, session.id).length > 0 + + // Only consider blank if we have loaded info, it shows 0 tokens, and no children + return sessionInfo !== undefined && sessionInfo.tokens === 0 && !hasChildren + } else if (session.title?.includes("subagent")) { + // Subagent + + const loadedSet = messagesLoaded().get(instanceId) || new Set() + if (!loadedSet.has(session.id)) return false + + if (session.messages.length === 0) return true + + const hasStreamingMessage = session.messages.some((msg) => msg.status === "streaming") + const isWaitingForPermission = session.pendingPermission === true + + // If streaming or waiting for permission, not blank + if (hasStreamingMessage || isWaitingForPermission) return false + + // Check if last message was a tool call + const lastMessage = session.messages[session.messages.length - 1] + const lastMessageWasToolCall = lastMessage.parts.some((part) => part.type === "tool") + + // Subagent is blank if last message was NOT a tool call + return !lastMessageWasToolCall + } else if (session.revert?.messageID) { + // Fork + const loadedSet = messagesLoaded().get(instanceId) || new Set() + if (!loadedSet.has(session.id)) return false + + if (session.messages.length === 0) return true + + const lastMessage = session.messages[session.messages.length - 1] + return lastMessage.id === session.revert.messageID + } + + return false // default to not saying it's blank, just to be safe +} + export { sessions, setSessions, @@ -259,4 +302,5 @@ export { isSessionBusy, isSessionMessagesLoading, getSessionInfo, + isBlankSession, }