From 80f31cd75eea7ba47b92aa5b479ef4024f21aa5f Mon Sep 17 00:00:00 2001 From: Quentin Date: Wed, 7 Jan 2026 22:48:15 +0100 Subject: [PATCH] fix: Signal SSE monitor reconnects on connection drop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Wrap streamSignalEvents in reconnection loop - Exponential backoff: 1s → 30s max - Log reconnection attempts - Respect abortSignal for clean shutdown Fixes #425 --- src/signal/monitor.ts | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/src/signal/monitor.ts b/src/signal/monitor.ts index 15bf8ed4b..b80cc88a9 100644 --- a/src/signal/monitor.ts +++ b/src/signal/monitor.ts @@ -524,16 +524,34 @@ export async function monitorSignalProvider( if (!queuedFinal) return; }; - await streamSignalEvents({ - baseUrl, - account, - abortSignal: opts.abortSignal, - onEvent: (event) => { - void handleEvent(event).catch((err) => { - runtime.error?.(`event handler failed: ${String(err)}`); + // Reconnection loop for SSE stream + const MAX_RETRY_DELAY = 30_000; // 30 seconds + const INITIAL_RETRY_DELAY = 1_000; // 1 second + let retryDelay = INITIAL_RETRY_DELAY; + + while (!opts.abortSignal?.aborted) { + try { + await streamSignalEvents({ + baseUrl, + account, + abortSignal: opts.abortSignal, + onEvent: (event) => { + void handleEvent(event).catch((err) => { + runtime.error?.(`event handler failed: ${String(err)}`); + }); + }, }); - }, - }); + // If streamSignalEvents returns normally, break (shouldn't happen normally) + break; + } catch (err) { + if (opts.abortSignal?.aborted) return; + runtime.log?.( + `Signal SSE connection lost, reconnecting in ${retryDelay / 1000}s...`, + ); + await new Promise((resolve) => setTimeout(resolve, retryDelay)); + retryDelay = Math.min(retryDelay * 2, MAX_RETRY_DELAY); + } + } } catch (err) { if (opts.abortSignal?.aborted) return; throw err;