diff --git a/src/hooks/shared/prompt-async-gate.test.ts b/src/hooks/shared/prompt-async-gate.test.ts index 260db9d67..cd943d3b4 100644 --- a/src/hooks/shared/prompt-async-gate.test.ts +++ b/src/hooks/shared/prompt-async-gate.test.ts @@ -56,6 +56,41 @@ describe("promptAsyncAfterSessionIdle", () => { expect(promptCalls).toBe(1) }) + test("#given settle is disabled and status is unavailable #when a second promptAsync starts after the first dispatch resolves #then the default dispatch hold keeps the session reserved", async () => { + // given + let promptCalls = 0 + const client = { + session: { + promptAsync: async () => { + promptCalls += 1 + }, + }, + } + + // when + const first = promptAsyncAfterSessionIdle({ + client, + sessionID: "ses_hold_after_dispatch", + input: { path: { id: "ses_hold_after_dispatch" }, body: { parts: [] } }, + source: "test:hold:first", + settleMs: 0, + }) + await new Promise((resolve) => setTimeout(resolve, 0)) + const second = await promptAsyncAfterSessionIdle({ + client, + sessionID: "ses_hold_after_dispatch", + input: { path: { id: "ses_hold_after_dispatch" }, body: { parts: [] } }, + source: "test:hold:second", + settleMs: 0, + }) + const firstResult = await first + + // then + expect(firstResult.status).toBe("dispatched") + expect(second.status).toBe("reserved") + expect(promptCalls).toBe(1) + }) + test("#given session.status reports busy #when an internal promptAsync is requested #then no prompt is sent", async () => { // given let promptCalls = 0 @@ -126,4 +161,39 @@ describe("promptAsyncAfterSessionIdle", () => { expect(second.status).toBe("reserved") expect(promptCalls).toBe(1) }) + + test("#given settle is disabled and status is unavailable #when a second prompt starts after the first dispatch resolves #then the default dispatch hold keeps the session reserved", async () => { + // given + let promptCalls = 0 + const client = { + session: { + prompt: async () => { + promptCalls += 1 + }, + }, + } + + // when + const first = promptAfterSessionIdle({ + client, + sessionID: "ses_prompt_hold_after_dispatch", + input: { path: { id: "ses_prompt_hold_after_dispatch" }, body: { parts: [] } }, + source: "test:prompt-hold:first", + settleMs: 0, + }) + await new Promise((resolve) => setTimeout(resolve, 0)) + const second = await promptAfterSessionIdle({ + client, + sessionID: "ses_prompt_hold_after_dispatch", + input: { path: { id: "ses_prompt_hold_after_dispatch" }, body: { parts: [] } }, + source: "test:prompt-hold:second", + settleMs: 0, + }) + const firstResult = await first + + // then + expect(firstResult.status).toBe("dispatched") + expect(second.status).toBe("reserved") + expect(promptCalls).toBe(1) + }) }) diff --git a/src/shared/prompt-async-gate.ts b/src/shared/prompt-async-gate.ts index 806b2178d..7f2f57553 100644 --- a/src/shared/prompt-async-gate.ts +++ b/src/shared/prompt-async-gate.ts @@ -60,9 +60,7 @@ export async function promptAsyncAfterSessionIdle(arg source, settleMs = DEFAULT_SESSION_IDLE_SETTLE_MS, } = args - const postDispatchHoldMs = args.postDispatchHoldMs ?? ( - settleMs > 0 ? DEFAULT_PROMPT_ASYNC_POST_DISPATCH_HOLD_MS : 0 - ) + const postDispatchHoldMs = args.postDispatchHoldMs ?? DEFAULT_PROMPT_ASYNC_POST_DISPATCH_HOLD_MS if (typeof client.session?.promptAsync !== "function") { log("[prompt-async-gate] promptAsync unavailable", { sessionID, source }) @@ -100,7 +98,7 @@ export async function promptAsyncAfterSessionIdle(arg log("[prompt-async-gate] promptAsync dispatching", { sessionID, source }) const response = await client.session.promptAsync(input) - if (canReadStatus) { + if (postDispatchHoldMs > 0) { await settleAfterSessionIdle(postDispatchHoldMs) } log("[prompt-async-gate] promptAsync dispatched", { sessionID, source }) @@ -132,9 +130,7 @@ export async function promptAfterSessionIdle(args: { source, settleMs = DEFAULT_SESSION_IDLE_SETTLE_MS, } = args - const postDispatchHoldMs = args.postDispatchHoldMs ?? ( - settleMs > 0 ? DEFAULT_PROMPT_ASYNC_POST_DISPATCH_HOLD_MS : 0 - ) + const postDispatchHoldMs = args.postDispatchHoldMs ?? DEFAULT_PROMPT_ASYNC_POST_DISPATCH_HOLD_MS if (typeof client.session?.prompt !== "function") { log("[prompt-async-gate] prompt unavailable", { sessionID, source }) @@ -172,7 +168,7 @@ export async function promptAfterSessionIdle(args: { log("[prompt-async-gate] prompt dispatching", { sessionID, source }) const response = await client.session.prompt(input) - if (canReadStatus) { + if (postDispatchHoldMs > 0) { await settleAfterSessionIdle(postDispatchHoldMs) } log("[prompt-async-gate] prompt dispatched", { sessionID, source })