From cc8115cf060f781478a22d8c6899e734284f664e Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Fri, 25 Sep 2026 17:06:39 -0700 Subject: [PATCH] fix(server): Grok replies finish when Grok finishes them, not when background work does When Grok's prompt ends while a background subagent or monitor keeps running, the ACP adapter holds the run open for that work but left the last assistant message streaming until the next item or the run's end. A short final reply like ROOT_DONE stayed "streaming" until the background work finished. Close the turn's text streams when the prompt settles into the deferred hold. The run stays open for background work as before. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../orchestration-v2/Adapters/AcpAdapterV2.ts | 3 +++ .../fixtures/grok_background_subagent/output.ts | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.ts b/apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.ts index e37fe223529e..b3df4d7b3b12 100644 --- a/apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.ts +++ b/apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.ts @@ -6938,6 +6938,9 @@ export function makeAcpAdapterV2(options: AcpAdapterV2Options): ProviderAdapterV ) { context.promptSettled = true; context.promptSettledStatus = status; + // The agent finished this prompt's reply. Background work + // holds the run open, not the text it already sent. + yield* closeTextStreams(context); return; } yield* finalizeTurn(context, status); diff --git a/apps/server/src/orchestration-v2/testkit/fixtures/grok_background_subagent/output.ts b/apps/server/src/orchestration-v2/testkit/fixtures/grok_background_subagent/output.ts index 8311aa92f574..77fcec010038 100644 --- a/apps/server/src/orchestration-v2/testkit/fixtures/grok_background_subagent/output.ts +++ b/apps/server/src/orchestration-v2/testkit/fixtures/grok_background_subagent/output.ts @@ -65,6 +65,23 @@ export function assertGrokBackgroundSubagentOutput( assert.isAbove(rootCompletedAt, subagentCompletedAt, "run 1 completed before the subagent"); assert.include(runAssistantTexts(projection, rootRun?.id), "ROOT_DONE"); + // Grok ends ROOT_DONE with its prompt; the subagent running on must not keep + // the reply streaming. + const rootDoneCompletedAt = result.domainEvents.findIndex( + (event) => + event.type === "turn-item.updated" && + event.payload.runId === rootRun?.id && + event.payload.type === "assistant_message" && + event.payload.text.trim() === "ROOT_DONE" && + event.payload.status === "completed", + ); + assert.isAtLeast(rootDoneCompletedAt, 0); + assert.isBelow( + rootDoneCompletedAt, + subagentCompletedAt, + "ROOT_DONE streamed until the subagent finished", + ); + // The subagent's own reply stays in its child thread. if (subagent?.childThreadId == null) { throw new Error("The background subagent is missing its child thread.");