From ff5a439d762a9462cb4c8142bf10968973eeffcf Mon Sep 17 00:00:00 2001 From: openperf <16864032@qq.com> Date: Wed, 3 Jun 2026 23:06:57 +0800 Subject: [PATCH] fix(context-engine): forward abortSignal through delegation bridge to runtime compaction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit delegateCompactionToRuntime built the CompactEmbeddedAgentSessionDirect params object without forwarding params.abortSignal. The ContextEngine compact() public API and CompactEmbeddedAgentSessionParams both carry an optional abortSignal, but the delegate bridge silently dropped it. When the user pressed stop during auto-compaction, the host aborted the wait via the signal, but the in-flight compaction LLM call continued to completion and wrote its summary to the session file. The next turn then saw a compacted session (e.g. 239→4 messages) the user had not consented to, causing model confusion about ongoing work and instructions. Fixes #89868 --- src/context-engine/context-engine.test.ts | 26 +++++++++++++++++++++++ src/context-engine/delegate.ts | 1 + 2 files changed, 27 insertions(+) diff --git a/src/context-engine/context-engine.test.ts b/src/context-engine/context-engine.test.ts index 354d37ca46b..024c0c2cfac 100644 --- a/src/context-engine/context-engine.test.ts +++ b/src/context-engine/context-engine.test.ts @@ -603,6 +603,32 @@ describe("Engine contract tests", () => { }); }); + it("delegateCompactionToRuntime forwards the caller abortSignal to the runtime (#89868)", async () => { + installCompactRuntimeSpy(); + const controller = new AbortController(); + await delegateCompactionToRuntime({ + sessionId: "s-abort", + sessionFile: "/tmp/session-abort.json", + tokenBudget: 4096, + abortSignal: controller.signal, + }); + + const compactRuntimeParams = requireCompactRuntimeParams(0); + expect(compactRuntimeParams.abortSignal).toBe(controller.signal); + }); + + it("delegateCompactionToRuntime passes undefined abortSignal when none supplied", async () => { + installCompactRuntimeSpy(); + await delegateCompactionToRuntime({ + sessionId: "s-no-abort", + sessionFile: "/tmp/session-no-abort.json", + tokenBudget: 4096, + }); + + const compactRuntimeParams = requireCompactRuntimeParams(0); + expect(compactRuntimeParams.abortSignal).toBeUndefined(); + }); + it("builds a normalized memory system prompt addition from the active memory prompt path", () => { registerMemoryPromptSection(({ citationsMode }) => [ "## Memory Recall", diff --git a/src/context-engine/delegate.ts b/src/context-engine/delegate.ts index 0e4088a11a6..85c679eb571 100644 --- a/src/context-engine/delegate.ts +++ b/src/context-engine/delegate.ts @@ -60,6 +60,7 @@ export async function delegateCompactionToRuntime( ...(currentTokenCount !== undefined ? { currentTokenCount } : {}), force: params.force, customInstructions: params.customInstructions, + abortSignal: params.abortSignal, workspaceDir: typeof runtimeContext.workspaceDir === "string" ? runtimeContext.workspaceDir : process.cwd(), });