From 095353ed43f64daa85a8a2b0d3bb7052ad905035 Mon Sep 17 00:00:00 2001 From: roboomp Date: Fri, 10 Jul 2026 03:05:54 +0000 Subject: [PATCH] fix(coding-agent): preserved multi-question timeout defaults Applied the timeout auto-selection before multi-question single-choice prompts auto-advance, and replaced pending test promises with Promise.withResolvers(). Refs #4995 --- packages/coding-agent/src/tools/ask.ts | 3 + .../coding-agent/test/ask-timeout.test.ts | 75 ++++++++++++++++++- 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/packages/coding-agent/src/tools/ask.ts b/packages/coding-agent/src/tools/ask.ts index 105284b38..26b9c9d67 100644 --- a/packages/coding-agent/src/tools/ask.ts +++ b/packages/coding-agent/src/tools/ask.ts @@ -642,6 +642,9 @@ async function askSingleQuestion( customInput = undefined; break; } + if (timedOut && selectedOptions.length === 0 && customInput === undefined) { + selectedOptions = getAutoSelectionOnTimeout(questionOptions, recommended); + } if (navigation?.allowForward) { return { selectedOptions, customInput, timedOut, navigation: "forward" }; } diff --git a/packages/coding-agent/test/ask-timeout.test.ts b/packages/coding-agent/test/ask-timeout.test.ts index be8e2ba8b..bd2bf36c7 100644 --- a/packages/coding-agent/test/ask-timeout.test.ts +++ b/packages/coding-agent/test/ask-timeout.test.ts @@ -50,7 +50,7 @@ describe("AskTool timeout", () => { vi.useFakeTimers(); const select = vi.fn((_title, _options, dialogOptions) => { dialogOptions?.onTimeoutStart?.(); - return new Promise(() => {}); + return Promise.withResolvers().promise; }); const abort = vi.fn(); const context = { @@ -106,7 +106,7 @@ describe("AskTool timeout", () => { const select = vi.fn((_title, _options, dialogOptions) => { dialogOptions?.onTimeoutStart?.(); resetTimeout = dialogOptions?.onTimeoutReset; - return new Promise(() => {}); + return Promise.withResolvers().promise; }); const abort = vi.fn(); const context = { @@ -170,7 +170,7 @@ describe("AskTool timeout", () => { let startTimeout: (() => void) | undefined; const select = vi.fn((_title, _options, dialogOptions) => { startTimeout = dialogOptions?.onTimeoutStart; - return new Promise(() => {}); + return Promise.withResolvers().promise; }); const abort = vi.fn(); const context = { @@ -228,6 +228,75 @@ describe("AskTool timeout", () => { expect(abort).not.toHaveBeenCalled(); }); + it("auto-selects timed-out single-choice questions before advancing multi-question asks", async () => { + vi.useFakeTimers(); + let callCount = 0; + const select = vi.fn((_title, _options, dialogOptions) => { + callCount += 1; + if (callCount === 1) { + dialogOptions?.onTimeoutStart?.(); + return Promise.withResolvers().promise; + } + return Promise.resolve("OAuth"); + }); + const abort = vi.fn(); + const context = { + hasUI: true, + ui: { + select, + editor: vi.fn(), + }, + abort, + } as unknown as AgentToolContext; + let result: AskExecutionResult | undefined; + let rejection: unknown; + + void createAskTool() + .execute( + "ask-timeout", + { + questions: [ + { + id: "db", + question: "Which database?", + options: [{ label: "SQLite" }, { label: "Postgres" }], + recommended: 1, + }, + { + id: "auth", + question: "Which auth?", + options: [{ label: "JWT" }, { label: "OAuth" }], + recommended: 0, + }, + ], + }, + undefined, + undefined, + context, + ) + .then( + value => { + result = value; + }, + error => { + rejection = error; + }, + ); + + await drainMicrotasks(); + vi.advanceTimersByTime(10); + await drainMicrotasks(); + await drainMicrotasks(); + + expect(rejection).toBeUndefined(); + expect(select).toHaveBeenCalledTimes(2); + expect(result?.details?.results?.[0]?.selectedOptions).toEqual(["Postgres"]); + expect(result?.details?.results?.[0]?.timedOut).toBe(true); + expect(result?.details?.results?.[1]?.selectedOptions).toEqual(["OAuth"]); + expect(result?.details?.results?.[1]?.timedOut).toBeUndefined(); + expect(abort).not.toHaveBeenCalled(); + }); + it("notifies callers when the selector countdown starts and resets", () => { vi.useFakeTimers(); const onTimeoutStart = vi.fn();