diff --git a/packages/provider-bridge-acp/src/bridge/bridge.test.ts b/packages/provider-bridge-acp/src/bridge/bridge.test.ts index 0ea207288..77ac9439b 100644 --- a/packages/provider-bridge-acp/src/bridge/bridge.test.ts +++ b/packages/provider-bridge-acp/src/bridge/bridge.test.ts @@ -2346,6 +2346,25 @@ describe("acp bridge", () => { expect(threadEventsOfType("turn/completed")).toHaveLength(1); }); + it("retries a busy prompt after the agent reports that the session is idle", async () => { + const promptLog = join(workspaceDir, "busy-prompt-log.jsonl"); + const { providerThreadId } = await startThread({ + envVars: { + FAKE_ACP_BUSY_RESPONSES: "1", + FAKE_ACP_PROMPT_LOG: promptLog, + }, + }); + const turnId = sendTurnRequest("turn/start", providerThreadId, { + input: [{ type: "text", text: "after-busy", mentions: [] }], + }); + await waitForResponse(turnId); + + const completed = await waitForTurnCompleted(); + expect(completed).toMatchObject({ status: "completed" }); + expect(loggedPrompts(promptLog)).toEqual(["after-busy", "after-busy"]); + expect(threadEventsOfType("provider/error")).toEqual([]); + }); + it("runs the builtin /compact command as compaction, not as a prompt", async () => { const promptLog = join(workspaceDir, "compact-prompt-log.jsonl"); const { providerThreadId } = await startThread({ diff --git a/packages/provider-bridge-acp/src/bridge/fake-acp-agent.mjs b/packages/provider-bridge-acp/src/bridge/fake-acp-agent.mjs index 270fce317..4afab391a 100755 --- a/packages/provider-bridge-acp/src/bridge/fake-acp-agent.mjs +++ b/packages/provider-bridge-acp/src/bridge/fake-acp-agent.mjs @@ -121,6 +121,9 @@ const fakeModels = [ ]; let activePromptId = null; +let remainingBusyResponses = Number( + process.env.FAKE_ACP_BUSY_RESPONSES ?? "0", +); let nextAgentRequestId = 1000; let selectedModel = "fake/default"; let selectedEffort = "none"; @@ -432,6 +435,29 @@ async function handlePrompt(message) { return; } + if (remainingBusyResponses > 0) { + remainingBusyResponses -= 1; + activePromptId = null; + send({ + jsonrpc: "2.0", + id: message.id, + error: { + code: -32003, + message: "Agent is busy", + data: { reason: "session_busy", hint: "wait" }, + }, + }); + setTimeout( + () => + notifyUpdate({ + sessionUpdate: "session_info_update", + title: "ready", + }), + 10, + ); + return; + } + if (text === "/compact") { // OpenCode treats this exact prompt as a provider-local control. const compactMessage = process.env.FAKE_ACP_COMPACT_AGENT_MESSAGE;