fix(prompt-gate): hold reservations after dispatch

This commit is contained in:
YeonGyu-Kim
2026-05-15 12:05:03 +09:00
parent c6e3b7e1f7
commit 05189700fb
2 changed files with 74 additions and 8 deletions
@@ -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)
})
})
+4 -8
View File
@@ -60,9 +60,7 @@ export async function promptAsyncAfterSessionIdle<TInput = PromptAsyncInput>(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<TInput = PromptAsyncInput>(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<TInput = PromptAsyncInput>(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<TInput = PromptAsyncInput>(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 })