fix(prompt-async-gate): timeout isSessionActive to prevent infinite hang on stale SDK status
- Wrap isSessionActive in withDispatchTimeout (capped at 5s) so a stuck OpenCode SDK status() call cannot block internal prompts forever. - Catch the timeout and treat session as inactive so the prompt can proceed rather than hanging indefinitely. - Add regression test: session.status that never resolves now times out and allows dispatch instead of hanging the test (and production). Refs: AGENTS.md internal-message-injection safety note
This commit is contained in:
@@ -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 = {
|
||||
|
||||
@@ -169,7 +169,19 @@ async function dispatchAfterSessionIdle<TInput>(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" }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user