fix: stop AgentBusyError on plan approval and loop/goal continuations
Plan approval dispatched the executor's first synthetic prompt without checking whether the agent was still streaming the post-resolve continuation (or a turn started by the approve-time compaction/clear), surfacing "Failed to finalize approved plan: ... Agent is already processing". Loop auto-submit and goal continuations hit the same throw via submitInteractiveInput, which always called prompt/promptCustomMessage without a streamingBehavior. #approvePlan now aborts any in-flight turn before the synthetic prompt, and submitInteractiveInput routes submissions through the steer/follow-up queue (streamingBehavior: "followUp") when the session is streaming. Non-streaming call shapes are unchanged. Extends the manual-/goal fix (#2454) to the continuation and plan-approval paths.
This commit is contained in:
@@ -46,6 +46,7 @@ describe("submitInteractiveInput", () => {
|
||||
const session = {
|
||||
prompt: vi.fn(async () => true),
|
||||
promptCustomMessage: vi.fn(async () => {}),
|
||||
isStreaming: false,
|
||||
};
|
||||
const input = createInput({ text: "resume now", started: true, synthetic: true });
|
||||
|
||||
@@ -67,6 +68,7 @@ describe("submitInteractiveInput", () => {
|
||||
const session = {
|
||||
prompt: vi.fn(async () => true),
|
||||
promptCustomMessage: vi.fn(async () => {}),
|
||||
isStreaming: false,
|
||||
};
|
||||
const input = createInput();
|
||||
|
||||
@@ -88,6 +90,7 @@ describe("submitInteractiveInput", () => {
|
||||
const session = {
|
||||
prompt: vi.fn(async () => true),
|
||||
promptCustomMessage: vi.fn(async () => {}),
|
||||
isStreaming: false,
|
||||
};
|
||||
const input = createInput({ text: "continue goal", customType: "goal-continuation" });
|
||||
|
||||
@@ -103,4 +106,56 @@ describe("submitInteractiveInput", () => {
|
||||
expect(mode.finishPendingSubmission).toHaveBeenCalledWith(input);
|
||||
expect(mode.showError).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("queues goal-continuation as followUp when streaming", async () => {
|
||||
const mode = {
|
||||
markPendingSubmissionStarted: vi.fn(() => true),
|
||||
finishPendingSubmission: vi.fn(),
|
||||
showError: vi.fn(),
|
||||
checkShutdownRequested: vi.fn(async () => {}),
|
||||
};
|
||||
const session = {
|
||||
prompt: vi.fn(async () => true),
|
||||
promptCustomMessage: vi.fn(async () => {}),
|
||||
isStreaming: true,
|
||||
};
|
||||
const input = createInput({ text: "continue goal", customType: "goal-continuation" });
|
||||
|
||||
await submitInteractiveInput(mode, session, input);
|
||||
|
||||
expect(session.prompt).not.toHaveBeenCalled();
|
||||
expect(session.promptCustomMessage).toHaveBeenCalledWith(
|
||||
{
|
||||
customType: "goal-continuation",
|
||||
content: "continue goal",
|
||||
display: false,
|
||||
attribution: "agent",
|
||||
},
|
||||
{ streamingBehavior: "followUp" },
|
||||
);
|
||||
expect(mode.finishPendingSubmission).toHaveBeenCalledWith(input);
|
||||
expect(mode.showError).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("queues a plain submission as followUp when streaming", async () => {
|
||||
const mode = {
|
||||
markPendingSubmissionStarted: vi.fn(() => true),
|
||||
finishPendingSubmission: vi.fn(),
|
||||
showError: vi.fn(),
|
||||
checkShutdownRequested: vi.fn(async () => {}),
|
||||
};
|
||||
const session = {
|
||||
prompt: vi.fn(async () => true),
|
||||
promptCustomMessage: vi.fn(async () => {}),
|
||||
isStreaming: true,
|
||||
};
|
||||
const input = createInput({ text: "loop prompt" });
|
||||
|
||||
await submitInteractiveInput(mode, session, input);
|
||||
|
||||
expect(session.prompt).toHaveBeenCalledWith("loop prompt", { images: undefined, streamingBehavior: "followUp" });
|
||||
expect(session.promptCustomMessage).not.toHaveBeenCalled();
|
||||
expect(mode.finishPendingSubmission).toHaveBeenCalledWith(input);
|
||||
expect(mode.showError).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user