fix(prompt-gate): harden internal prompt dispatch
This commit is contained in:
@@ -238,7 +238,7 @@ describe("injectContinuation", () => {
|
||||
expect(capturedBody?.variant).toBe("max")
|
||||
})
|
||||
|
||||
test("#given a peer-message hold survives an unrelated release #when todo continuation injects #then it skips and clears in-flight state", async () => {
|
||||
test("#given a peer-message hold survives an unrelated release #when todo continuation injects #then it queues behind the peer message", async () => {
|
||||
// given
|
||||
const sessionID = "ses_todo_reserved_by_peer_message"
|
||||
let promptCalls = 0
|
||||
@@ -286,6 +286,50 @@ describe("injectContinuation", () => {
|
||||
expect(peerMessageResult.status).toBe("dispatched")
|
||||
expect(promptCalls).toBe(1)
|
||||
expect(state.inFlight).toBe(false)
|
||||
expect(state.lastInjectedAt).toBe(0)
|
||||
expect(state.lastInjectedAt).toBeGreaterThan(0)
|
||||
})
|
||||
|
||||
test("#given promptAsync may have accepted before EOF #when continuation injection observes the failure #then it records an optimistic injection", async () => {
|
||||
// given
|
||||
const state = {
|
||||
inFlight: false,
|
||||
lastInjectedAt: 0,
|
||||
awaitingPostInjectionProgressCheck: false,
|
||||
consecutiveFailures: 2,
|
||||
}
|
||||
let promptCalls = 0
|
||||
const ctx = {
|
||||
directory: "/tmp/test",
|
||||
client: {
|
||||
session: {
|
||||
todo: async () => ({ data: [{ id: "1", content: "todo", status: "pending", priority: "high" }] }),
|
||||
promptAsync: async () => {
|
||||
promptCalls += 1
|
||||
throw new Error("JSON Parse error: Unexpected EOF")
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
const sessionStateStore = {
|
||||
getExistingState: () => state,
|
||||
}
|
||||
|
||||
// when
|
||||
await injectContinuation({
|
||||
ctx: ctx as never,
|
||||
sessionID: "ses_continuation_eof",
|
||||
resolvedInfo: {
|
||||
agent: "Sisyphus - Ultraworker",
|
||||
model: { providerID: "anthropic", modelID: "claude-sonnet-4-20250514" },
|
||||
},
|
||||
sessionStateStore: sessionStateStore as never,
|
||||
})
|
||||
|
||||
// then
|
||||
expect(promptCalls).toBe(1)
|
||||
expect(state.inFlight).toBe(false)
|
||||
expect(state.awaitingPostInjectionProgressCheck).toBe(true)
|
||||
expect(state.consecutiveFailures).toBe(0)
|
||||
expect(state.lastInjectedAt).toBeGreaterThan(0)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
} from "../../features/claude-code-session-state"
|
||||
import {
|
||||
createInternalAgentContinuationTextPart,
|
||||
isAmbiguousPromptDispatchFailure,
|
||||
normalizeSDKResponse,
|
||||
resolveInheritedPromptTools,
|
||||
} from "../../shared"
|
||||
@@ -22,7 +23,7 @@ import {
|
||||
normalizeAgentForPromptKey,
|
||||
stripAgentListSortPrefix,
|
||||
} from "../../shared/agent-display-names"
|
||||
import { dispatchInternalPrompt } from "../shared/prompt-async-gate"
|
||||
import { dispatchInternalPrompt, isInternalPromptDispatchAccepted } from "../shared/prompt-async-gate"
|
||||
|
||||
import {
|
||||
CONTINUATION_PROMPT,
|
||||
@@ -208,7 +209,7 @@ ${todoList}`
|
||||
if (promptResult.status === "failed") {
|
||||
throw promptResult.error
|
||||
}
|
||||
if (promptResult.status !== "dispatched") {
|
||||
if (!isInternalPromptDispatchAccepted(promptResult)) {
|
||||
log(`[${HOOK_NAME}] Injection skipped by promptAsync gate`, { sessionID, status: promptResult.status })
|
||||
if (injectionState) {
|
||||
injectionState.inFlight = false
|
||||
@@ -216,7 +217,7 @@ ${todoList}`
|
||||
return
|
||||
}
|
||||
|
||||
log(`[${HOOK_NAME}] Injection successful`, { sessionID })
|
||||
log(`[${HOOK_NAME}] Injection successful`, { sessionID, status: promptResult.status })
|
||||
if (injectionState) {
|
||||
injectionState.inFlight = false
|
||||
injectionState.lastInjectedAt = Date.now()
|
||||
@@ -228,6 +229,11 @@ ${todoList}`
|
||||
if (injectionState) {
|
||||
injectionState.inFlight = false
|
||||
injectionState.lastInjectedAt = Date.now()
|
||||
if (isAmbiguousPromptDispatchFailure(error)) {
|
||||
injectionState.awaitingPostInjectionProgressCheck = true
|
||||
injectionState.consecutiveFailures = 0
|
||||
return
|
||||
}
|
||||
injectionState.consecutiveFailures = (injectionState.consecutiveFailures ?? 0) + 1
|
||||
|
||||
const errorObj = error instanceof Error
|
||||
|
||||
Reference in New Issue
Block a user