fix mobile chat run reconnect (#993)
This commit is contained in:
@@ -72,6 +72,19 @@ export interface RunEvent {
|
||||
}
|
||||
}
|
||||
|
||||
export interface ResumeSessionPayload {
|
||||
session_id: string
|
||||
messages: any[]
|
||||
isWorking: boolean
|
||||
isAborting?: boolean
|
||||
events: Array<{ event: string; data: RunEvent }>
|
||||
inputTokens?: number
|
||||
outputTokens?: number
|
||||
contextTokens?: number
|
||||
queueLength?: number
|
||||
queueMessages?: RunEvent['queued_messages']
|
||||
}
|
||||
|
||||
// ============================
|
||||
// Socket.IO chat run connection
|
||||
// ============================
|
||||
@@ -80,6 +93,12 @@ let chatRunSocket: Socket | null = null
|
||||
let globalListenersRegistered = false
|
||||
let chatRunSocketProfile: string | null = null
|
||||
|
||||
const TRANSIENT_DISCONNECT_REASONS = new Set<string>([
|
||||
'transport close',
|
||||
'transport error',
|
||||
'ping timeout',
|
||||
])
|
||||
|
||||
/**
|
||||
* Session event handlers map
|
||||
* Maps session_id to event handling functions for isolating concurrent session streams
|
||||
@@ -597,7 +616,7 @@ function removeSocketListener(socket: Socket, event: string, handler: (...args:
|
||||
*/
|
||||
export function resumeSession(
|
||||
sessionId: string,
|
||||
onResumed: (data: { session_id: string; messages: any[]; isWorking: boolean; isAborting?: boolean; events: any[]; inputTokens?: number; outputTokens?: number; contextTokens?: number; queueLength?: number; queueMessages?: RunEvent['queued_messages'] }) => void,
|
||||
onResumed: (data: ResumeSessionPayload) => void,
|
||||
profile?: string | null,
|
||||
): Socket {
|
||||
const socket = connectChatRun(profile)
|
||||
@@ -614,6 +633,9 @@ export function startRunViaSocket(
|
||||
onDone: () => void,
|
||||
onError: (err: Error) => void,
|
||||
onStarted?: (runId: string) => void,
|
||||
options?: {
|
||||
onReconnectResume?: (data: ResumeSessionPayload) => void
|
||||
},
|
||||
): { abort: () => void } {
|
||||
const sid = body.session_id
|
||||
if (!sid) {
|
||||
@@ -622,24 +644,6 @@ export function startRunViaSocket(
|
||||
|
||||
let closed = false
|
||||
const socket = connectChatRun(body.profile)
|
||||
const handleSocketError = (err: Error) => {
|
||||
if (closed) return
|
||||
closed = true
|
||||
sessionEventHandlers.delete(sid)
|
||||
onError(err)
|
||||
}
|
||||
socket.once('connect_error', handleSocketError)
|
||||
const handleSocketDisconnect = (reason: string) => {
|
||||
if (closed || reason === 'io client disconnect') return
|
||||
handleSocketError(new Error(`Socket disconnected: ${reason}`))
|
||||
}
|
||||
socket.once('disconnect', handleSocketDisconnect)
|
||||
|
||||
const removeTerminalSocketListeners = () => {
|
||||
removeSocketListener(socket, 'connect_error', handleSocketError)
|
||||
removeSocketListener(socket, 'disconnect', handleSocketDisconnect)
|
||||
}
|
||||
|
||||
if (sessionEventHandlers.has(sid)) {
|
||||
socket.emit('run', body)
|
||||
return {
|
||||
@@ -651,6 +655,66 @@ export function startRunViaSocket(
|
||||
}
|
||||
}
|
||||
|
||||
let sawTransientDisconnect = false
|
||||
let removeTerminalSocketListeners: () => void = () => {}
|
||||
let reconnectResumeHandler: ((data: ResumeSessionPayload) => void) | null = null
|
||||
|
||||
const clearReconnectResumeHandler = () => {
|
||||
if (!reconnectResumeHandler) return
|
||||
removeSocketListener(socket, 'resumed', reconnectResumeHandler)
|
||||
reconnectResumeHandler = null
|
||||
}
|
||||
|
||||
const emitReconnectResume = () => {
|
||||
clearReconnectResumeHandler()
|
||||
if (options?.onReconnectResume) {
|
||||
reconnectResumeHandler = (data: ResumeSessionPayload) => {
|
||||
clearReconnectResumeHandler()
|
||||
if (closed || data.session_id !== sid) return
|
||||
options.onReconnectResume?.(data)
|
||||
}
|
||||
socket.on('resumed', reconnectResumeHandler)
|
||||
}
|
||||
socket.emit('resume', { session_id: sid, ...(body.profile ? { profile: body.profile } : {}) })
|
||||
}
|
||||
|
||||
const handleSocketError = (err: Error) => {
|
||||
if (closed) return
|
||||
closed = true
|
||||
removeTerminalSocketListeners()
|
||||
sessionEventHandlers.delete(sid)
|
||||
onError(err)
|
||||
}
|
||||
const handleSocketConnectError = (err: Error) => {
|
||||
if (closed) return
|
||||
if (sawTransientDisconnect) return
|
||||
handleSocketError(err)
|
||||
}
|
||||
socket.on('connect_error', handleSocketConnectError)
|
||||
const handleSocketDisconnect = (reason: string) => {
|
||||
if (closed || reason === 'io client disconnect') return
|
||||
if (TRANSIENT_DISCONNECT_REASONS.has(reason)) {
|
||||
sawTransientDisconnect = true
|
||||
return
|
||||
}
|
||||
handleSocketError(new Error(`Socket disconnected: ${reason}`))
|
||||
}
|
||||
socket.on('disconnect', handleSocketDisconnect)
|
||||
|
||||
const handleSocketReconnect = () => {
|
||||
if (closed || !sawTransientDisconnect) return
|
||||
sawTransientDisconnect = false
|
||||
emitReconnectResume()
|
||||
}
|
||||
socket.on('connect', handleSocketReconnect)
|
||||
|
||||
removeTerminalSocketListeners = () => {
|
||||
clearReconnectResumeHandler()
|
||||
removeSocketListener(socket, 'connect_error', handleSocketConnectError)
|
||||
removeSocketListener(socket, 'disconnect', handleSocketDisconnect)
|
||||
removeSocketListener(socket, 'connect', handleSocketReconnect)
|
||||
}
|
||||
|
||||
// Define event handlers for this session
|
||||
const handlers = {
|
||||
onMessageDelta: (evt: RunEvent) => {
|
||||
|
||||
Reference in New Issue
Block a user