Two fixes:
1. Failure latch (Codex ID=2964357928): add previewSendFailed boolean that is set true
in both the initial-send and patch-edit catch blocks (alongside stopPatchInterval).
schedulePatch() checks it before re-arming the interval, so subsequent onPartialReply
calls during a run with a permanent failure (missing permission, DM-creation error)
no longer recreate the timer and retry indefinitely.
2. patchInterval in onSettled guard (Codex ID=2964357925): onSettled now triggers
cleanup when patchInterval is non-null, even if streamMessageId and patchSending
are still falsy. This covers the window between schedulePatch arming the interval
and the first 200ms tick flipping patchSending — if the run ends in that window
(same-target messaging-tool sends, empty/heartbeat replies), the interval is now
stopped and the pending state is cleared.