diff --git a/src/hooks/shared/prompt-async-gate.test.ts b/src/hooks/shared/prompt-async-gate.test.ts index 450b70de4..7f6dc6044 100644 --- a/src/hooks/shared/prompt-async-gate.test.ts +++ b/src/hooks/shared/prompt-async-gate.test.ts @@ -476,6 +476,34 @@ describe("promptAsyncAfterSessionIdle", () => { expect(promptCalls).toBe(1) }) + test("#given session.status never resolves #when promptAsync is requested #then isSessionActive times out and dispatch is attempted", async () => { + // given + let promptCalls = 0 + const client = { + session: { + status: async () => new Promise(() => {}), + promptAsync: async () => { + promptCalls += 1 + }, + }, + } + + // when + const result = await promptAsyncAfterSessionIdle({ + client, + sessionID: "ses_status_hang", + input: { path: { id: "ses_status_hang" }, body: { parts: [] } }, + source: "test:status-hang", + settleMs: 0, + postDispatchHoldMs: 0, + dispatchTimeoutMs: 50, + }) + + // then + expect(result.status).toBe("dispatched") + expect(promptCalls).toBe(1) + }, 2000) + test("#given SDK prompt depends on its session receiver #when the gate dispatches #then method binding is preserved", async () => { // given const session = { diff --git a/src/shared/prompt-async-gate.ts b/src/shared/prompt-async-gate.ts index 3822ccf11..ff53a16cd 100644 --- a/src/shared/prompt-async-gate.ts +++ b/src/shared/prompt-async-gate.ts @@ -169,7 +169,19 @@ async function dispatchAfterSessionIdle(args: { await settleAfterSessionIdle(settleMs) } - if (canReadStatus && await isSessionActive(client, sessionID)) { + let sessionActive = false + if (canReadStatus) { + try { + sessionActive = await withDispatchTimeout( + isSessionActive(client, sessionID), + Math.min(dispatchTimeoutMs, 5000), + `[prompt-async-gate] ${sessionName} isSessionActive`, + ) + } catch { + sessionActive = false + } + } + if (sessionActive) { log(`[prompt-async-gate] ${sessionName} skipped because session is active`, { sessionID, source }) return { status: "active" } }