From b32d4c5255c5d3ad089bf9a8c6dc25cf9e46c563 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 5 May 2026 09:06:45 +0100 Subject: [PATCH] fix: avoid media completion fallback while announce pending --- src/agents/subagent-announce-delivery.test.ts | 42 +++++++++++++++++++ src/agents/subagent-announce-delivery.ts | 21 +++++++++- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/agents/subagent-announce-delivery.test.ts b/src/agents/subagent-announce-delivery.test.ts index 83496572695..a859bda773f 100644 --- a/src/agents/subagent-announce-delivery.test.ts +++ b/src/agents/subagent-announce-delivery.test.ts @@ -1271,6 +1271,48 @@ describe("deliverSubagentAnnouncement completion delivery", () => { ); }); + it("does not fallback while generated media announce-agent run is still pending", async () => { + const callGateway = createGatewayMock({ + runId: "video_generate:task-123:ok", + status: "accepted", + acceptedAt: Date.now(), + }); + const sendMessage = createSendMessageMock(); + const result = await deliverSlackChannelAnnouncement({ + callGateway, + sendMessage, + sessionId: "requester-session-channel", + isActive: false, + expectsCompletionMessage: true, + directIdempotencyKey: "announce-channel-media-pending", + sourceTool: "video_generate", + internalEvents: [ + { + type: "task_completion", + source: "video_generation", + childSessionKey: "video_generate:task-123", + childSessionId: "task-123", + announceType: "video generation task", + taskLabel: "lobster trailer", + status: "ok", + statusLabel: "completed successfully", + result: "Generated 1 video.\nMEDIA:/tmp/lobster-trailer.mp4", + mediaUrls: ["/tmp/lobster-trailer.mp4"], + replyInstruction: "Deliver the generated video through the message tool.", + }, + ], + }); + + expect(result).toEqual( + expect.objectContaining({ + delivered: true, + path: "direct", + }), + ); + expect(callGateway).toHaveBeenCalled(); + expect(sendMessage).not.toHaveBeenCalled(); + }); + it("uses a direct channel fallback when announce-agent returns no visible output", async () => { const callGateway = createGatewayMock({ result: { diff --git a/src/agents/subagent-announce-delivery.ts b/src/agents/subagent-announce-delivery.ts index 461cc127ecb..f44fe6b5668 100644 --- a/src/agents/subagent-announce-delivery.ts +++ b/src/agents/subagent-announce-delivery.ts @@ -689,6 +689,14 @@ function hasGatewayAgentMessagingToolDelivery(response: unknown): boolean { return Boolean(result && hasMessagingToolDeliveryEvidence(result)); } +function isGatewayAgentRunPending(response: unknown): boolean { + if (!response || typeof response !== "object") { + return false; + } + const status = (response as { status?: unknown }).status; + return status === "accepted" || status === "in_flight" || status === "started"; +} + function inferCompletionChatType(params: { requesterSessionKey: string; targetRequesterSessionKey: string; @@ -1047,7 +1055,11 @@ async function sendSubagentAnnounceDirectly(params: { throw err; } - if (shouldSendCompletionFallback(directAnnounceResponse, completionFallbackText)) { + const directAnnounceStillPending = isGatewayAgentRunPending(directAnnounceResponse); + if ( + !directAnnounceStillPending && + shouldSendCompletionFallback(directAnnounceResponse, completionFallbackText) + ) { const didFallback = await sendCompletionFallback({ cfg, channel: deliveryTarget.channel, @@ -1068,6 +1080,13 @@ async function sendSubagentAnnounceDirectly(params: { } } + if (directAnnounceStillPending) { + return { + delivered: true, + path: "direct", + }; + } + if ( requiresMessageToolDelivery && !hasGatewayAgentMessagingToolDelivery(directAnnounceResponse)