Track session status via SSE updates
This commit is contained in:
@@ -13,6 +13,7 @@ import type {
|
|||||||
EventSessionError,
|
EventSessionError,
|
||||||
EventSessionIdle,
|
EventSessionIdle,
|
||||||
EventSessionUpdated,
|
EventSessionUpdated,
|
||||||
|
EventSessionStatus,
|
||||||
} from "@opencode-ai/sdk"
|
} from "@opencode-ai/sdk"
|
||||||
import { serverEvents } from "./server-events"
|
import { serverEvents } from "./server-events"
|
||||||
import type {
|
import type {
|
||||||
@@ -134,6 +135,9 @@ class SSEManager {
|
|||||||
case "session.idle":
|
case "session.idle":
|
||||||
this.onSessionIdle?.(instanceId, event as EventSessionIdle)
|
this.onSessionIdle?.(instanceId, event as EventSessionIdle)
|
||||||
break
|
break
|
||||||
|
case "session.status":
|
||||||
|
this.onSessionStatus?.(instanceId, event as EventSessionStatus)
|
||||||
|
break
|
||||||
case "permission.updated":
|
case "permission.updated":
|
||||||
this.onPermissionUpdated?.(instanceId, event as EventPermissionUpdated)
|
this.onPermissionUpdated?.(instanceId, event as EventPermissionUpdated)
|
||||||
break
|
break
|
||||||
@@ -171,6 +175,7 @@ class SSEManager {
|
|||||||
onSessionError?: (instanceId: string, event: EventSessionError) => void
|
onSessionError?: (instanceId: string, event: EventSessionError) => void
|
||||||
onTuiToast?: (instanceId: string, event: TuiToastEvent) => void
|
onTuiToast?: (instanceId: string, event: TuiToastEvent) => void
|
||||||
onSessionIdle?: (instanceId: string, event: EventSessionIdle) => void
|
onSessionIdle?: (instanceId: string, event: EventSessionIdle) => void
|
||||||
|
onSessionStatus?: (instanceId: string, event: EventSessionStatus) => void
|
||||||
onPermissionUpdated?: (instanceId: string, event: EventPermissionUpdated) => void
|
onPermissionUpdated?: (instanceId: string, event: EventPermissionUpdated) => void
|
||||||
onPermissionReplied?: (instanceId: string, event: EventPermissionReplied) => void
|
onPermissionReplied?: (instanceId: string, event: EventPermissionReplied) => void
|
||||||
onLspUpdated?: (instanceId: string, event: EventLspUpdated) => void
|
onLspUpdated?: (instanceId: string, event: EventLspUpdated) => void
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
setProviders,
|
setProviders,
|
||||||
setSessionInfoByInstance,
|
setSessionInfoByInstance,
|
||||||
setSessions,
|
setSessions,
|
||||||
|
setSessionStatus,
|
||||||
sessions,
|
sessions,
|
||||||
loading,
|
loading,
|
||||||
setLoading,
|
setLoading,
|
||||||
@@ -27,6 +28,7 @@ import {
|
|||||||
import { DEFAULT_MODEL_OUTPUT_LIMIT, getDefaultModel, isModelValid } from "./session-models"
|
import { DEFAULT_MODEL_OUTPUT_LIMIT, getDefaultModel, isModelValid } from "./session-models"
|
||||||
import { normalizeMessagePart } from "./message-v2/normalizers"
|
import { normalizeMessagePart } from "./message-v2/normalizers"
|
||||||
import { updateSessionInfo } from "./message-v2/session-info"
|
import { updateSessionInfo } from "./message-v2/session-info"
|
||||||
|
import { deriveSessionStatusFromMessages } from "./session-status"
|
||||||
import { seedSessionMessagesV2 } from "./message-v2/bridge"
|
import { seedSessionMessagesV2 } from "./message-v2/bridge"
|
||||||
import { messageStoreBus } from "./message-v2/bus"
|
import { messageStoreBus } from "./message-v2/bus"
|
||||||
import { clearCacheForSession } from "../lib/global-cache"
|
import { clearCacheForSession } from "../lib/global-cache"
|
||||||
@@ -82,6 +84,10 @@ async function fetchSessions(instanceId: string): Promise<void> {
|
|||||||
for (const apiSession of response.data) {
|
for (const apiSession of response.data) {
|
||||||
const existingSession = existingSessions?.get(apiSession.id)
|
const existingSession = existingSessions?.get(apiSession.id)
|
||||||
|
|
||||||
|
const compactingFlag = (apiSession.time as (Session["time"] & { compacting?: number | boolean }) | undefined)?.compacting
|
||||||
|
const isCompacting = typeof compactingFlag === "number" ? compactingFlag > 0 : Boolean(compactingFlag)
|
||||||
|
const existingStatus = existingSession?.status
|
||||||
|
|
||||||
sessionMap.set(apiSession.id, {
|
sessionMap.set(apiSession.id, {
|
||||||
id: apiSession.id,
|
id: apiSession.id,
|
||||||
instanceId,
|
instanceId,
|
||||||
@@ -89,6 +95,7 @@ async function fetchSessions(instanceId: string): Promise<void> {
|
|||||||
parentId: apiSession.parentID || null,
|
parentId: apiSession.parentID || null,
|
||||||
agent: existingSession?.agent ?? "",
|
agent: existingSession?.agent ?? "",
|
||||||
model: existingSession?.model ?? { providerId: "", modelId: "" },
|
model: existingSession?.model ?? { providerId: "", modelId: "" },
|
||||||
|
status: isCompacting ? "compacting" : (existingStatus ?? "idle"),
|
||||||
version: apiSession.version,
|
version: apiSession.version,
|
||||||
time: {
|
time: {
|
||||||
...apiSession.time,
|
...apiSession.time,
|
||||||
@@ -183,6 +190,7 @@ async function createSession(instanceId: string, agent?: string): Promise<Sessio
|
|||||||
parentId: null,
|
parentId: null,
|
||||||
agent: selectedAgent,
|
agent: selectedAgent,
|
||||||
model: defaultModel,
|
model: defaultModel,
|
||||||
|
status: "idle",
|
||||||
version: response.data.version,
|
version: response.data.version,
|
||||||
time: {
|
time: {
|
||||||
...response.data.time,
|
...response.data.time,
|
||||||
@@ -290,6 +298,7 @@ async function forkSession(
|
|||||||
providerId: info.model?.providerID || "",
|
providerId: info.model?.providerID || "",
|
||||||
modelId: info.model?.modelID || "",
|
modelId: info.model?.modelID || "",
|
||||||
},
|
},
|
||||||
|
status: "idle",
|
||||||
version: "0",
|
version: "0",
|
||||||
time: info.time ? { ...info.time } : { created: Date.now(), updated: Date.now() },
|
time: info.time ? { ...info.time } : { created: Date.now(), updated: Date.now() },
|
||||||
revert: info.revert
|
revert: info.revert
|
||||||
@@ -606,6 +615,11 @@ async function loadMessages(instanceId: string, sessionId: string, force = false
|
|||||||
}
|
}
|
||||||
seedSessionMessagesV2(instanceId, sessionForV2, messages, messagesInfo)
|
seedSessionMessagesV2(instanceId, sessionForV2, messages, messagesInfo)
|
||||||
|
|
||||||
|
if (!alreadyLoaded) {
|
||||||
|
const nextStatus = deriveSessionStatusFromMessages(instanceId, sessionId)
|
||||||
|
setSessionStatus(instanceId, sessionId, nextStatus)
|
||||||
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error("Failed to load messages:", error)
|
log.error("Failed to load messages:", error)
|
||||||
throw error
|
throw error
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import type {
|
|||||||
EventSessionError,
|
EventSessionError,
|
||||||
EventSessionIdle,
|
EventSessionIdle,
|
||||||
EventSessionUpdated,
|
EventSessionUpdated,
|
||||||
|
EventSessionStatus,
|
||||||
} from "@opencode-ai/sdk"
|
} from "@opencode-ai/sdk"
|
||||||
import type { MessageStatus } from "./message-v2/types"
|
import type { MessageStatus } from "./message-v2/types"
|
||||||
|
|
||||||
@@ -19,11 +20,11 @@ import { getLogger } from "../lib/logger"
|
|||||||
import { showToastNotification, ToastVariant } from "../lib/notifications"
|
import { showToastNotification, ToastVariant } from "../lib/notifications"
|
||||||
import { instances, addPermissionToQueue, removePermissionFromQueue } from "./instances"
|
import { instances, addPermissionToQueue, removePermissionFromQueue } from "./instances"
|
||||||
import { showAlertDialog } from "./alerts"
|
import { showAlertDialog } from "./alerts"
|
||||||
|
import { createClientSession, Session, SessionStatus } from "../types/session"
|
||||||
import { sessions, setSessions, withSession } from "./session-state"
|
import { sessions, setSessions, withSession } from "./session-state"
|
||||||
import { normalizeMessagePart } from "./message-v2/normalizers"
|
import { normalizeMessagePart } from "./message-v2/normalizers"
|
||||||
import { updateSessionInfo } from "./message-v2/session-info"
|
import { updateSessionInfo } from "./message-v2/session-info"
|
||||||
|
|
||||||
const log = getLogger("sse")
|
|
||||||
import { loadMessages } from "./session-api"
|
import { loadMessages } from "./session-api"
|
||||||
import { setSessionCompactionState } from "./session-compaction"
|
import { setSessionCompactionState } from "./session-compaction"
|
||||||
import {
|
import {
|
||||||
@@ -39,6 +40,9 @@ import {
|
|||||||
import { messageStoreBus } from "./message-v2/bus"
|
import { messageStoreBus } from "./message-v2/bus"
|
||||||
import type { InstanceMessageStore } from "./message-v2/instance-store"
|
import type { InstanceMessageStore } from "./message-v2/instance-store"
|
||||||
|
|
||||||
|
const log = getLogger("sse")
|
||||||
|
const pendingSessionFetches = new Map<string, Promise<void>>()
|
||||||
|
|
||||||
interface TuiToastEvent {
|
interface TuiToastEvent {
|
||||||
type: "tui.toast.show"
|
type: "tui.toast.show"
|
||||||
properties: {
|
properties: {
|
||||||
@@ -51,8 +55,83 @@ interface TuiToastEvent {
|
|||||||
|
|
||||||
const ALLOWED_TOAST_VARIANTS = new Set<ToastVariant>(["info", "success", "warning", "error"])
|
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, bumpUpdated = false) {
|
||||||
|
withSession(instanceId, sessionId, (session) => {
|
||||||
|
session.status = status
|
||||||
|
if (bumpUpdated) {
|
||||||
|
session.time = { ...(session.time ?? {}), updated: Date.now() }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchSessionInfo(instanceId: string, sessionId: string): Promise<Session | null> {
|
||||||
|
const instance = instances().get(instanceId)
|
||||||
|
if (!instance?.client) return null
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await instance.client.session.get({ path: { id: sessionId } })
|
||||||
|
if (!response.data) return null
|
||||||
|
|
||||||
|
const fetched = createClientSession(response.data, instanceId)
|
||||||
|
|
||||||
|
setSessions((prev) => {
|
||||||
|
const next = new Map(prev)
|
||||||
|
const instanceSessions = new Map(next.get(instanceId) ?? [])
|
||||||
|
const existing = instanceSessions.get(sessionId)
|
||||||
|
instanceSessions.set(sessionId, {
|
||||||
|
...fetched,
|
||||||
|
agent: existing?.agent ?? fetched.agent,
|
||||||
|
model: existing?.model ?? fetched.model,
|
||||||
|
status: existing?.status ?? fetched.status,
|
||||||
|
pendingPermission: existing?.pendingPermission ?? fetched.pendingPermission,
|
||||||
|
})
|
||||||
|
next.set(instanceId, instanceSessions)
|
||||||
|
return next
|
||||||
|
})
|
||||||
|
|
||||||
|
return fetched
|
||||||
|
} catch (error) {
|
||||||
|
log.error("Failed to fetch session info", error)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureSessionStatus(instanceId: string, sessionId: string, status: SessionStatus, bumpUpdated = false) {
|
||||||
|
const instanceSessions = sessions().get(instanceId)
|
||||||
|
const existing = instanceSessions?.get(sessionId)
|
||||||
|
if (existing) {
|
||||||
|
applySessionStatus(instanceId, sessionId, status, bumpUpdated)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const key = `${instanceId}:${sessionId}`
|
||||||
|
if (pendingSessionFetches.has(key)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const pending = (async () => {
|
||||||
|
const fetched = await fetchSessionInfo(instanceId, sessionId)
|
||||||
|
if (!fetched) return
|
||||||
|
applySessionStatus(instanceId, sessionId, status, bumpUpdated)
|
||||||
|
})()
|
||||||
|
|
||||||
|
pendingSessionFetches.set(key, pending)
|
||||||
|
void pending.finally(() => pendingSessionFetches.delete(key))
|
||||||
|
}
|
||||||
|
|
||||||
type MessageRole = "user" | "assistant"
|
type MessageRole = "user" | "assistant"
|
||||||
|
|
||||||
|
|
||||||
function resolveMessageRole(info?: MessageInfo | null): MessageRole {
|
function resolveMessageRole(info?: MessageInfo | null): MessageRole {
|
||||||
return info?.role === "user" ? "user" : "assistant"
|
return info?.role === "user" ? "user" : "assistant"
|
||||||
}
|
}
|
||||||
@@ -74,7 +153,6 @@ function findPendingMessageId(
|
|||||||
|
|
||||||
function handleMessageUpdate(instanceId: string, event: MessageUpdateEvent | MessagePartUpdatedEvent): void {
|
function handleMessageUpdate(instanceId: string, event: MessageUpdateEvent | MessagePartUpdatedEvent): void {
|
||||||
const instanceSessions = sessions().get(instanceId)
|
const instanceSessions = sessions().get(instanceId)
|
||||||
if (!instanceSessions) return
|
|
||||||
|
|
||||||
if (event.type === "message.part.updated") {
|
if (event.type === "message.part.updated") {
|
||||||
const rawPart = event.properties?.part
|
const rawPart = event.properties?.part
|
||||||
@@ -90,8 +168,13 @@ function handleMessageUpdate(instanceId: string, event: MessageUpdateEvent | Mes
|
|||||||
const messageId = typeof part.messageID === "string" ? part.messageID : fallbackMessageId
|
const messageId = typeof part.messageID === "string" ? part.messageID : fallbackMessageId
|
||||||
if (!sessionId || !messageId) return
|
if (!sessionId || !messageId) return
|
||||||
|
|
||||||
const session = instanceSessions.get(sessionId)
|
const session = instanceSessions?.get(sessionId)
|
||||||
if (!session) return
|
if (!session) {
|
||||||
|
ensureSessionStatus(instanceId, sessionId, "working", true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
applySessionStatus(instanceId, sessionId, "working", true)
|
||||||
|
|
||||||
const store = messageStoreBus.getOrCreate(instanceId)
|
const store = messageStoreBus.getOrCreate(instanceId)
|
||||||
const role: MessageRole = resolveMessageRole(messageInfo)
|
const role: MessageRole = resolveMessageRole(messageInfo)
|
||||||
@@ -135,10 +218,16 @@ function handleMessageUpdate(instanceId: string, event: MessageUpdateEvent | Mes
|
|||||||
const messageId = typeof info.id === "string" ? info.id : undefined
|
const messageId = typeof info.id === "string" ? info.id : undefined
|
||||||
if (!sessionId || !messageId) return
|
if (!sessionId || !messageId) return
|
||||||
|
|
||||||
const session = instanceSessions.get(sessionId)
|
const session = instanceSessions?.get(sessionId)
|
||||||
if (!session) return
|
if (!session) {
|
||||||
|
ensureSessionStatus(instanceId, sessionId, "working", true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
applySessionStatus(instanceId, sessionId, "working", true)
|
||||||
|
|
||||||
const store = messageStoreBus.getOrCreate(instanceId)
|
const store = messageStoreBus.getOrCreate(instanceId)
|
||||||
|
|
||||||
const role: MessageRole = info.role === "user" ? "user" : "assistant"
|
const role: MessageRole = info.role === "user" ? "user" : "assistant"
|
||||||
const hasError = Boolean((info as any).error)
|
const hasError = Boolean((info as any).error)
|
||||||
const status: MessageStatus = hasError ? "error" : "complete"
|
const status: MessageStatus = hasError ? "error" : "complete"
|
||||||
@@ -180,8 +269,7 @@ function handleSessionUpdate(instanceId: string, event: EventSessionUpdated): vo
|
|||||||
const isCompacting = typeof compactingFlag === "number" ? compactingFlag > 0 : Boolean(compactingFlag)
|
const isCompacting = typeof compactingFlag === "number" ? compactingFlag > 0 : Boolean(compactingFlag)
|
||||||
setSessionCompactionState(instanceId, info.id, isCompacting)
|
setSessionCompactionState(instanceId, info.id, isCompacting)
|
||||||
|
|
||||||
const instanceSessions = sessions().get(instanceId)
|
const instanceSessions = sessions().get(instanceId) ?? new Map<string, Session>()
|
||||||
if (!instanceSessions) return
|
|
||||||
|
|
||||||
const existingSession = instanceSessions.get(info.id)
|
const existingSession = instanceSessions.get(info.id)
|
||||||
|
|
||||||
@@ -196,6 +284,7 @@ function handleSessionUpdate(instanceId: string, event: EventSessionUpdated): vo
|
|||||||
providerId: "",
|
providerId: "",
|
||||||
modelId: "",
|
modelId: "",
|
||||||
},
|
},
|
||||||
|
status: isCompacting ? "compacting" : "idle",
|
||||||
version: info.version || "0",
|
version: info.version || "0",
|
||||||
time: info.time
|
time: info.time
|
||||||
? { ...info.time }
|
? { ...info.time }
|
||||||
@@ -203,7 +292,7 @@ function handleSessionUpdate(instanceId: string, event: EventSessionUpdated): vo
|
|||||||
created: Date.now(),
|
created: Date.now(),
|
||||||
updated: Date.now(),
|
updated: Date.now(),
|
||||||
},
|
},
|
||||||
} as any
|
} as Session
|
||||||
|
|
||||||
setSessions((prev) => {
|
setSessions((prev) => {
|
||||||
const next = new Map(prev)
|
const next = new Map(prev)
|
||||||
@@ -227,6 +316,7 @@ function handleSessionUpdate(instanceId: string, event: EventSessionUpdated): vo
|
|||||||
const updatedSession = {
|
const updatedSession = {
|
||||||
...existingSession,
|
...existingSession,
|
||||||
title: info.title || existingSession.title,
|
title: info.title || existingSession.title,
|
||||||
|
status: isCompacting ? "compacting" : (existingSession.status ?? "idle"),
|
||||||
time: mergedTime,
|
time: mergedTime,
|
||||||
revert: info.revert
|
revert: info.revert
|
||||||
? {
|
? {
|
||||||
@@ -249,13 +339,23 @@ function handleSessionUpdate(instanceId: string, event: EventSessionUpdated): vo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSessionIdle(_instanceId: string, event: EventSessionIdle): void {
|
function handleSessionIdle(instanceId: string, event: EventSessionIdle): void {
|
||||||
const sessionId = event.properties?.sessionID
|
const sessionId = event.properties?.sessionID
|
||||||
if (!sessionId) return
|
if (!sessionId) return
|
||||||
|
|
||||||
|
ensureSessionStatus(instanceId, sessionId, "idle")
|
||||||
log.info(`[SSE] Session idle: ${sessionId}`)
|
log.info(`[SSE] Session idle: ${sessionId}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleSessionStatus(instanceId: string, event: EventSessionStatus): void {
|
||||||
|
const sessionId = event.properties?.sessionID
|
||||||
|
if (!sessionId) return
|
||||||
|
|
||||||
|
const status = mapSdkSessionStatus(event.properties.status)
|
||||||
|
ensureSessionStatus(instanceId, sessionId, status, status === "working")
|
||||||
|
log.info(`[SSE] Session status updated: ${sessionId}`, { status })
|
||||||
|
}
|
||||||
|
|
||||||
function handleSessionCompacted(instanceId: string, event: EventSessionCompacted): void {
|
function handleSessionCompacted(instanceId: string, event: EventSessionCompacted): void {
|
||||||
const sessionID = event.properties?.sessionID
|
const sessionID = event.properties?.sessionID
|
||||||
if (!sessionID) return
|
if (!sessionID) return
|
||||||
@@ -263,6 +363,7 @@ function handleSessionCompacted(instanceId: string, event: EventSessionCompacted
|
|||||||
log.info(`[SSE] Session compacted: ${sessionID}`)
|
log.info(`[SSE] Session compacted: ${sessionID}`)
|
||||||
|
|
||||||
setSessionCompactionState(instanceId, sessionID, false)
|
setSessionCompactionState(instanceId, sessionID, false)
|
||||||
|
ensureSessionStatus(instanceId, sessionID, "idle")
|
||||||
|
|
||||||
withSession(instanceId, sessionID, (session) => {
|
withSession(instanceId, sessionID, (session) => {
|
||||||
const time = { ...(session.time ?? {}) }
|
const time = { ...(session.time ?? {}) }
|
||||||
@@ -368,6 +469,7 @@ export {
|
|||||||
handleSessionCompacted,
|
handleSessionCompacted,
|
||||||
handleSessionError,
|
handleSessionError,
|
||||||
handleSessionIdle,
|
handleSessionIdle,
|
||||||
|
handleSessionStatus,
|
||||||
handleSessionUpdate,
|
handleSessionUpdate,
|
||||||
handleTuiToast,
|
handleTuiToast,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { createSignal } from "solid-js"
|
import { createSignal } from "solid-js"
|
||||||
|
|
||||||
import type { Session, Agent, Provider } from "../types/session"
|
import type { Session, SessionStatus, Agent, Provider } from "../types/session"
|
||||||
import { deleteSession, loadMessages } from "./session-api"
|
import { deleteSession, loadMessages } from "./session-api"
|
||||||
import { showToastNotification } from "../lib/notifications"
|
import { showToastNotification } from "../lib/notifications"
|
||||||
import { messageStoreBus } from "./message-v2/bus"
|
import { messageStoreBus } from "./message-v2/bus"
|
||||||
@@ -157,6 +157,11 @@ function setSessionCompactionState(instanceId: string, sessionId: string, isComp
|
|||||||
const time = { ...(session.time ?? {}) }
|
const time = { ...(session.time ?? {}) }
|
||||||
time.compacting = isCompacting ? Date.now() : 0
|
time.compacting = isCompacting ? Date.now() : 0
|
||||||
session.time = time
|
session.time = time
|
||||||
|
if (isCompacting) {
|
||||||
|
session.status = "compacting"
|
||||||
|
} else if (session.status === "compacting") {
|
||||||
|
session.status = "idle"
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,6 +204,12 @@ function clearActiveParentSession(instanceId: string): void {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setSessionStatus(instanceId: string, sessionId: string, status: SessionStatus): void {
|
||||||
|
withSession(instanceId, sessionId, (session) => {
|
||||||
|
session.status = status
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function getActiveParentSession(instanceId: string): Session | null {
|
function getActiveParentSession(instanceId: string): Session | null {
|
||||||
const parentId = activeParentSessionId().get(instanceId)
|
const parentId = activeParentSessionId().get(instanceId)
|
||||||
if (!parentId) return null
|
if (!parentId) return null
|
||||||
@@ -380,6 +391,7 @@ export {
|
|||||||
withSession,
|
withSession,
|
||||||
setSessionCompactionState,
|
setSessionCompactionState,
|
||||||
setSessionPendingPermission,
|
setSessionPendingPermission,
|
||||||
|
setSessionStatus,
|
||||||
setActiveSession,
|
setActiveSession,
|
||||||
|
|
||||||
setActiveParentSession,
|
setActiveParentSession,
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ function isAssistantStillGeneratingRecord(record: MessageRecord, info?: MessageI
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function getSessionStatus(instanceId: string, sessionId: string): SessionStatus {
|
export function deriveSessionStatusFromMessages(instanceId: string, sessionId: string): SessionStatus {
|
||||||
const session = getSession(instanceId, sessionId)
|
const session = getSession(instanceId, sessionId)
|
||||||
if (!session) {
|
if (!session) {
|
||||||
return "idle"
|
return "idle"
|
||||||
@@ -162,6 +162,14 @@ export function getSessionStatus(instanceId: string, sessionId: string): Session
|
|||||||
return "idle"
|
return "idle"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getSessionStatus(instanceId: string, sessionId: string): SessionStatus {
|
||||||
|
const session = getSession(instanceId, sessionId)
|
||||||
|
if (!session) {
|
||||||
|
return "idle"
|
||||||
|
}
|
||||||
|
return session.status ?? deriveSessionStatusFromMessages(instanceId, sessionId)
|
||||||
|
}
|
||||||
|
|
||||||
export function isSessionBusy(instanceId: string, sessionId: string): boolean {
|
export function isSessionBusy(instanceId: string, sessionId: string): boolean {
|
||||||
const status = getSessionStatus(instanceId, sessionId)
|
const status = getSessionStatus(instanceId, sessionId)
|
||||||
return status === "working" || status === "compacting"
|
return status === "working" || status === "compacting"
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import {
|
|||||||
setActiveParentSession,
|
setActiveParentSession,
|
||||||
setActiveSession,
|
setActiveSession,
|
||||||
setSessionDraftPrompt,
|
setSessionDraftPrompt,
|
||||||
|
setSessionStatus,
|
||||||
} from "./session-state"
|
} from "./session-state"
|
||||||
|
|
||||||
import { getDefaultModel } from "./session-models"
|
import { getDefaultModel } from "./session-models"
|
||||||
@@ -56,6 +57,7 @@ import {
|
|||||||
handleSessionCompacted,
|
handleSessionCompacted,
|
||||||
handleSessionError,
|
handleSessionError,
|
||||||
handleSessionIdle,
|
handleSessionIdle,
|
||||||
|
handleSessionStatus,
|
||||||
handleSessionUpdate,
|
handleSessionUpdate,
|
||||||
handleTuiToast,
|
handleTuiToast,
|
||||||
} from "./session-events"
|
} from "./session-events"
|
||||||
@@ -68,6 +70,7 @@ sseManager.onSessionUpdate = handleSessionUpdate
|
|||||||
sseManager.onSessionCompacted = handleSessionCompacted
|
sseManager.onSessionCompacted = handleSessionCompacted
|
||||||
sseManager.onSessionError = handleSessionError
|
sseManager.onSessionError = handleSessionError
|
||||||
sseManager.onSessionIdle = handleSessionIdle
|
sseManager.onSessionIdle = handleSessionIdle
|
||||||
|
sseManager.onSessionStatus = handleSessionStatus
|
||||||
sseManager.onTuiToast = handleTuiToast
|
sseManager.onTuiToast = handleTuiToast
|
||||||
sseManager.onPermissionUpdated = handlePermissionUpdated
|
sseManager.onPermissionUpdated = handlePermissionUpdated
|
||||||
sseManager.onPermissionReplied = handlePermissionReplied
|
sseManager.onPermissionReplied = handlePermissionReplied
|
||||||
@@ -109,6 +112,7 @@ export {
|
|||||||
setActiveParentSession,
|
setActiveParentSession,
|
||||||
setActiveSession,
|
setActiveSession,
|
||||||
setSessionDraftPrompt,
|
setSessionDraftPrompt,
|
||||||
|
setSessionStatus,
|
||||||
updateSessionAgent,
|
updateSessionAgent,
|
||||||
updateSessionModel,
|
updateSessionModel,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ export interface Session
|
|||||||
}
|
}
|
||||||
version: string // Include version from SDK Session
|
version: string // Include version from SDK Session
|
||||||
pendingPermission?: boolean // Indicates if session is waiting on user permission
|
pendingPermission?: boolean // Indicates if session is waiting on user permission
|
||||||
|
status: SessionStatus // Single source of truth for session status
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adapter function to convert SDK Session to client Session
|
// Adapter function to convert SDK Session to client Session
|
||||||
@@ -42,6 +43,7 @@ export function createClientSession(
|
|||||||
parentId: sdkSession.parentID || null,
|
parentId: sdkSession.parentID || null,
|
||||||
agent,
|
agent,
|
||||||
model,
|
model,
|
||||||
|
status: "idle",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user