fix(shared): add timeout to isSessionActive to prevent infinite hang
The SDK client disables fetch timeout (req.timeout = false). When the opencode server is slow or unresponsive, client.session.status() hangs forever, blocking the entire dispatchToHooks chain (sequential await) and deadlocking the plugin. Add a 5s timeout wrapper around the status call in isSessionActive(). On timeout, the catch block returns false (treat as inactive), letting the caller proceed instead of hanging indefinitely. The timeout parameter is exposed for testing to avoid 5s test delays. Refs: .debugging/status-timeout-hang.md
This commit is contained in:
@@ -1,9 +1,24 @@
|
||||
export const DEFAULT_SESSION_IDLE_SETTLE_MS = 150
|
||||
export const DEFAULT_SESSION_STATUS_TIMEOUT_MS = 5_000
|
||||
|
||||
export function settleAfterSessionIdle(ms = DEFAULT_SESSION_IDLE_SETTLE_MS): Promise<void> {
|
||||
return ms > 0 ? new Promise((resolve) => setTimeout(resolve, ms)) : Promise.resolve()
|
||||
}
|
||||
|
||||
function withStatusTimeout<T>(promise: Promise<T>, timeoutMs: number): Promise<T> {
|
||||
let timeoutID: ReturnType<typeof setTimeout> | undefined
|
||||
const timeoutPromise = new Promise<T>((_resolve, reject) => {
|
||||
timeoutID = setTimeout(() => {
|
||||
reject(new Error(`session.status() timed out after ${timeoutMs}ms`))
|
||||
}, timeoutMs)
|
||||
})
|
||||
return Promise.race([promise, timeoutPromise]).finally(() => {
|
||||
if (timeoutID !== undefined) {
|
||||
clearTimeout(timeoutID)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type SessionStatusClient = {
|
||||
session?: {
|
||||
status?: () => Promise<unknown>
|
||||
@@ -32,13 +47,20 @@ export function isActiveSessionStatusType(statusType: string): boolean {
|
||||
return ACTIVE_SESSION_STATUSES.has(statusType)
|
||||
}
|
||||
|
||||
export async function isSessionActive(client: SessionStatusClient, sessionID: string): Promise<boolean> {
|
||||
export async function isSessionActive(
|
||||
client: SessionStatusClient,
|
||||
sessionID: string,
|
||||
statusTimeoutMs: number = DEFAULT_SESSION_STATUS_TIMEOUT_MS,
|
||||
): Promise<boolean> {
|
||||
if (typeof client.session?.status !== "function") {
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
const statusResult = await client.session.status()
|
||||
const statusResult = await withStatusTimeout(
|
||||
client.session.status(),
|
||||
statusTimeoutMs,
|
||||
)
|
||||
const status = getSessionStatusPayload(statusResult)[sessionID]
|
||||
if (!isRecord(status)) {
|
||||
return false
|
||||
|
||||
Reference in New Issue
Block a user