From 7d219bd6e7bd593ecea1a9b1be9bc1df20f9c02c Mon Sep 17 00:00:00 2001 From: Vincent Koc <25068+vincentkoc@users.noreply.github.com> Date: Fri, 19 Jun 2026 14:21:37 +0800 Subject: [PATCH] fix(copilot): bound background compaction waits --- extensions/copilot/src/attempt.test.ts | 25 +++++++++++++++++++++++++ extensions/copilot/src/attempt.ts | 9 +++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/extensions/copilot/src/attempt.test.ts b/extensions/copilot/src/attempt.test.ts index 7e4a1bc3f2d2..00d98d54b83b 100644 --- a/extensions/copilot/src/attempt.test.ts +++ b/extensions/copilot/src/attempt.test.ts @@ -441,6 +441,31 @@ describe("runCopilotAttempt", () => { expect(beforeCompaction.mock.calls[0]?.[0]).not.toHaveProperty("messages"); }); + it("bounds successful background compaction when the SDK omits completion", async () => { + vi.useFakeTimers(); + const sdk = makeFakeSdk({ + onCreateSession: (session) => { + session.sendAndWait.mockImplementationOnce(async () => { + session.emit("session.compaction_start", {}); + return makeAssistantMessageEvent("done"); + }); + }, + }); + const pool = makeFakePool(sdk); + + const attempt = runCopilotAttempt(makeParams(), { pool }); + await vi.advanceTimersByTimeAsync(0); + await vi.advanceTimersByTimeAsync(180_000); + const result = await attempt; + + expect(result.timedOut).toBe(false); + expect(result.promptError).toBeUndefined(); + expect(sdk.sessions[0]?.rpc.history.cancelBackgroundCompaction).toHaveBeenCalledTimes(1); + expect(sdk.sessions[0]?.disconnect).toHaveBeenCalledTimes(1); + expect(sdk.client.deleteSession).toHaveBeenCalledWith("sess-1"); + expect(pool.release).toHaveBeenCalledTimes(1); + }); + it("defers and cancels compaction when the caller aborts after a turn result", async () => { const controller = new AbortController(); const onDeferredCompaction = vi.fn(); diff --git a/extensions/copilot/src/attempt.ts b/extensions/copilot/src/attempt.ts index c09aba9c11f4..c62fe06d7842 100644 --- a/extensions/copilot/src/attempt.ts +++ b/extensions/copilot/src/attempt.ts @@ -783,7 +783,11 @@ export async function runCopilotAttempt( settled = true; const compactionCompletionOutcome = waitForCompactionCompletion && !aborted && !params.abortSignal?.aborted - ? await awaitCompactionCompletionOrAbort(bridge!, params.abortSignal) + ? await awaitCompactionCompletionBeforeDeadline({ + abortSignal: params.abortSignal, + bridge: bridge!, + timeoutMs: resolveCompactionTimeoutMs(input.config), + }) : undefined; const deferCompactionCleanup = bridge?.isCompacting() && @@ -791,12 +795,13 @@ export async function runCopilotAttempt( handle && (timedOut || compactionCompletionOutcome === "aborted" || + compactionCompletionOutcome === "deadline" || params.abortSignal?.aborted === true); if (deferCompactionCleanup && bridge && session && handle) { timedOutDuringCompaction ||= timedOut; const cleanupAbort = new AbortController(); const abortCleanup = () => cleanupAbort.abort(); - if (params.abortSignal?.aborted) { + if (params.abortSignal?.aborted || compactionCompletionOutcome === "deadline") { abortCleanup(); } else { params.abortSignal?.addEventListener("abort", abortCleanup, { once: true });