fix(mattermost): clean up streaming preview when no final payload arrives; stop retrying failed initial sends

Two fixes:

1. onSettled orphan cleanup (Codex ID=2963834802): add cleanup in the streaming
   dispatcher's onSettled callback for cases where the reply pipeline produces no
   final payload — e.g. messaging-tool sends suppressed by agent-runner-payloads.ts,
   or empty/heartbeat responses. Without this, onPartialReply could create a preview
   post that is never finalized or deleted. The cleanup mirrors the existing logic in
   #43041 (P5).

2. Initial-send retry storm (Codex ID=2963834806): call stopPatchInterval() in the
   sendMessageMattermost catch block, mirroring the existing fix for patchMattermostPost
   failures. Without this, a failed initial post attempt (missing post permission, DM
   creation failure, etc.) causes the 200ms interval to retry indefinitely for the rest
   of the response, flooding the API and gateway logs.
This commit is contained in:
teconomix 2026-03-20 06:03:16 +00:00
parent 38f3e76f97
commit e2c4d8bd45

View File

@ -1499,6 +1499,10 @@ export async function monitorMattermostProvider(opts: MonitorMattermostOpts = {}
runtime.log?.(`stream-patch started ${streamMessageId}`);
} catch (err) {
logVerboseMessage(`mattermost stream-patch send failed: ${String(err)}`);
// Stop retrying on initial-send failure (e.g. missing post permission,
// DM-channel creation failure). Without this the interval keeps firing
// every 200 ms and flooding the API/logs for the rest of the response.
stopPatchInterval();
}
} else {
try {
@ -1696,6 +1700,21 @@ export async function monitorMattermostProvider(opts: MonitorMattermostOpts = {}
dispatcher,
onSettled: () => {
markDispatchIdle();
// Clean up any streaming preview that was never finalized — this happens when
// the reply pipeline produces no final payload (e.g. messaging-tool sends that
// are suppressed, or empty/heartbeat responses). Without this, onPartialReply
// can create a Mattermost post that is never deleted or patched with final text.
if (streamMessageId && blockStreamingClient) {
stopPatchInterval();
const orphanId = streamMessageId;
streamMessageId = null;
pendingPatchText = "";
lastSentText = "";
patchSending = false;
void deleteMattermostPost(blockStreamingClient, orphanId).catch(() => {
// Best-effort — the run is already complete.
});
}
},
run: () =>
core.channel.reply.dispatchReplyFromConfig({