fix(mattermost): wait for in-flight preview send before divergent delivery; stop retrying failed patches
Two fixes: 1. Divergent-target flush (Codex ID=2962544342): when replyTargetDiverged is true the flush is skipped to avoid creating a preview in the wrong thread, but the patch interval was not stopped and any in-flight first sendMessageMattermost was not awaited. If that send resolved after the divergent delivery returned, it created a stray preview post with no cleanup path. Fix: always stop the interval and wait for patchSending to settle (up to 2s) even on the divergent path, so streamMessageId is populated if the send resolves during this window and the orphan cleanup below can capture and delete it. 2. Patch-failure retry storm (Codex ID=2962544347): after a patchMattermostPost failure in the schedulePatch interval, streamMessageId remained set and every subsequent 200ms tick retried the same failing request, spamming the API until final delivery. Fix: call stopPatchInterval() in the catch block so retries stop immediately. The preview stays frozen at its last successful text; deliver() will patch or replace it when the final reply arrives.
This commit is contained in:
parent
59db1c2b67
commit
38f3e76f97
@ -1510,6 +1510,10 @@ export async function monitorMattermostProvider(opts: MonitorMattermostOpts = {}
|
||||
runtime.log?.(`stream-patch edited ${streamMessageId}`);
|
||||
} catch (err) {
|
||||
logVerboseMessage(`mattermost stream-patch edit failed: ${String(err)}`);
|
||||
// Stop retrying on patch failure — the post may not be editable
|
||||
// (missing edit_post permission, deleted, etc.). The preview stays
|
||||
// frozen; final delivery will patch or replace it via deliver().
|
||||
stopPatchInterval();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
@ -1537,10 +1541,22 @@ export async function monitorMattermostProvider(opts: MonitorMattermostOpts = {}
|
||||
const replyTargetDiverged =
|
||||
finalReplyToId !== effectiveReplyToId && payload.replyToId != null;
|
||||
|
||||
// Flush any pending partial-reply patch before final delivery —
|
||||
// but only when the reply stays in the same thread as the preview post.
|
||||
if (isFinal && blockStreamingClient && !replyTargetDiverged) {
|
||||
await flushPendingPatch();
|
||||
if (isFinal && blockStreamingClient) {
|
||||
if (replyTargetDiverged) {
|
||||
// Divergent target: don't flush (we don't want to create a preview in
|
||||
// the wrong thread), but do stop the interval and wait for any in-flight
|
||||
// tick/send to settle. This ensures streamMessageId is populated if the
|
||||
// first sendMessageMattermost resolves during this window, so the orphan
|
||||
// cleanup below can capture and delete it.
|
||||
stopPatchInterval();
|
||||
const deadline = Date.now() + 2000;
|
||||
while (patchSending && Date.now() < deadline) {
|
||||
await new Promise<void>((r) => setTimeout(r, 20));
|
||||
}
|
||||
} else {
|
||||
// Same thread: flush pending patches normally.
|
||||
await flushPendingPatch();
|
||||
}
|
||||
}
|
||||
|
||||
// Final + streaming active: patch the streamed message with authoritative
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user