From 59db1c2b67ced7bac1507a2d0020d558c6b190f4 Mon Sep 17 00:00:00 2001 From: teconomix Date: Thu, 19 Mar 2026 20:17:51 +0000 Subject: [PATCH] fix(mattermost): deliver media-only final before deleting streamed preview When the final payload has no text (media-only), the in-place text patch branch is skipped, but the streamed preview post was previously left in the channel alongside the attachment. The orphanedStreamId capture now always holds the stream post ID (not only on divergent-target paths), and a new branch delivers the media payload first and deletes the stale preview only after delivery succeeds. If delivery fails, the preview stays visible as a fallback. --- .../mattermost/src/mattermost/monitor.ts | 33 +++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/extensions/mattermost/src/mattermost/monitor.ts b/extensions/mattermost/src/mattermost/monitor.ts index f801e680c3c..03f11673d7c 100644 --- a/extensions/mattermost/src/mattermost/monitor.ts +++ b/extensions/mattermost/src/mattermost/monitor.ts @@ -1598,17 +1598,13 @@ export async function monitorMattermostProvider(opts: MonitorMattermostOpts = {} if (isFinal) { stopPatchInterval(); // Capture and clear the stream ID so normal delivery below can proceed. - // If the reply target diverged we hold the orphan ID and delete it - // *after* the replacement message is successfully sent (see below). - const orphanedStreamId = replyTargetDiverged ? streamMessageId : null; + const orphanedStreamId = streamMessageId; streamMessageId = null; pendingPatchText = ""; lastSentText = ""; patchSending = false; - if (!orphanedStreamId) { - // No divergence — fall through to normal delivery. - } else { + if (replyTargetDiverged && orphanedStreamId) { // Divergent target: deliver to the correct thread first, then clean // up the orphan. If delivery fails the user keeps the partial preview. await deliverMattermostReplyPayload({ @@ -1630,6 +1626,31 @@ export async function monitorMattermostProvider(opts: MonitorMattermostOpts = {} } return; } + + if (orphanedStreamId && !payload.text) { + // Media-only final with a streamed text-preview: deliver media first, + // then delete the orphaned preview so users don't see stale partial + // text alongside the attachment. If delivery fails, the preview stays. + await deliverMattermostReplyPayload({ + core, + cfg, + payload, + to, + accountId: account.accountId, + agentId: route.agentId, + replyToId: finalReplyToId, + textLimit, + tableMode, + sendMessage: sendMessageMattermost, + }); + try { + await deleteMattermostPost(blockStreamingClient!, orphanedStreamId); + } catch { + // Ignore — media was already delivered. + } + return; + } + // Otherwise fall through to normal delivery. } // Normal delivery — streaming not active or non-final partial.