From 786de3eca2a55cce2e40afd24a4571dbb6836763 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sun, 12 Apr 2026 16:53:34 +0100 Subject: [PATCH] fix(gateway): keep tick broadcasts non-droppable (#65436) * fix(gateway): keep tick broadcasts non-droppable * Update CHANGELOG.md --- CHANGELOG.md | 1 + src/gateway/server-maintenance.test.ts | 19 +++++++++++++++++++ src/gateway/server-maintenance.ts | 2 +- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc7dc8b5ccd..71ce69e328b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Docs: https://docs.openclaw.ai - Gateway/auth: blank the shipped example gateway credential in `.env.example` and fail startup when a copied placeholder token or password is still configured, so operators cannot accidentally launch with a publicly known secret. (#64586) Thanks @navarrotech and @vincentkoc. - Memory/active-memory+dreaming: keep active-memory recall runs on the strongest resolved channel, consume managed dreaming heartbeat events exactly once, stop dreaming from re-ingesting its own narrative transcripts, and add explicit repair/dedupe recovery flows in CLI, doctor, and the Dreams UI. +- Gateway/keepalive: stop marking WebSocket tick broadcasts as droppable so slow or backpressured clients do not self-disconnect with `tick timeout` while long-running work is still alive. (#65256) Thanks @100yenadmin and @vincentkoc. - Matrix/mentions: keep room mention gating strict while accepting visible `@displayName` Matrix URI labels, so `requireMention` works for non-OpenClaw Matrix clients again. (#64796) Thanks @hclsys. - Doctor: warn when on-disk agent directories still exist under `~/.openclaw/agents//agent` but the matching `agents.list[]` entries are missing from config. (#65113) Thanks @neeravmakwana. - Telegram: route approval button callback queries onto a separate sequentializer lane so plugin approval clicks can resolve immediately instead of deadlocking behind the blocked agent turn. (#64979) Thanks @nk3750. diff --git a/src/gateway/server-maintenance.test.ts b/src/gateway/server-maintenance.test.ts index c6455412f26..553b04118b1 100644 --- a/src/gateway/server-maintenance.test.ts +++ b/src/gateway/server-maintenance.test.ts @@ -104,6 +104,25 @@ describe("startGatewayMaintenanceTimers", () => { stopMaintenanceTimers(timers); }); + it("broadcasts tick keepalives without dropIfSlow", async () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date("2026-04-12T00:00:00Z")); + const { startGatewayMaintenanceTimers } = await import("./server-maintenance.js"); + const broadcast = vi.fn(); + + const timers = startGatewayMaintenanceTimers({ + ...createMaintenanceTimerDeps(), + broadcast, + }); + + broadcast.mockClear(); + await vi.advanceTimersByTimeAsync(30_000); + + expect(broadcast).toHaveBeenCalledWith("tick", { ts: Date.now() }); + + stopMaintenanceTimers(timers); + }); + it("skips overlapping media cleanup runs", async () => { vi.useFakeTimers(); let resolveCleanup = () => {}; diff --git a/src/gateway/server-maintenance.ts b/src/gateway/server-maintenance.ts index a00a573e87a..ce4fe1d7956 100644 --- a/src/gateway/server-maintenance.ts +++ b/src/gateway/server-maintenance.ts @@ -61,7 +61,7 @@ export function startGatewayMaintenanceTimers(params: { // periodic keepalive const tickInterval = setInterval(() => { const payload = { ts: Date.now() }; - params.broadcast("tick", payload, { dropIfSlow: true }); + params.broadcast("tick", payload); params.nodeSendToAllSubscribed("tick", payload); }, TICK_INTERVAL_MS);