From 1600c1726e6be67136ffa50cd2b390d574ebc596 Mon Sep 17 00:00:00 2001 From: Gustavo Madeira Santana Date: Sun, 29 Mar 2026 19:31:01 -0400 Subject: [PATCH] Control UI: reconnect on seq gaps --- CHANGELOG.md | 1 + ui/src/ui/app-gateway.node.test.ts | 11 ++++++++--- ui/src/ui/app-gateway.ts | 3 ++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40e9f6e21b7..16ffa269653 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -211,6 +211,7 @@ Docs: https://docs.openclaw.ai - Memory/builtin: keep memory-file indexing active in FTS-only mode (no embedding provider) so forced reindexes no longer swap in an empty index and wipe existing memory chunks. (#42714) Thanks @asamimei. - CLI/status: detect node-only hosts in `openclaw status` and `openclaw status --all`, show the configured remote gateway target instead of a false local `ECONNREFUSED`, and suppress contradictory local-gateway diagnosis output. - Gateway/SQLite transient handling: keep unhandled `SQLITE_CANTOPEN`, `SQLITE_BUSY`, `SQLITE_LOCKED`, and `SQLITE_IOERR` failures non-fatal in the global rejection handler so macOS LaunchAgent restarts do not enter a crash-throttle loop. (#57018) +- Control UI/gateway: reconnect the browser client when gateway event sequence gaps are detected, so stale non-chat state recovers automatically instead of only telling the user to refresh. (#23912) thanks @Olshansk. ## 2026.3.24 diff --git a/ui/src/ui/app-gateway.node.test.ts b/ui/src/ui/app-gateway.node.test.ts index 86a269d1bc7..2b908cc672d 100644 --- a/ui/src/ui/app-gateway.node.test.ts +++ b/ui/src/ui/app-gateway.node.test.ts @@ -131,12 +131,17 @@ function createHost() { assistantAgentId: null, serverVersion: null, sessionKey: "main", + basePath: "", + chatMessage: "", chatMessages: [], + chatAttachments: [], + chatQueue: [], chatToolMessages: [], chatStreamSegments: [], chatStream: null, chatStreamStartedAt: null, chatRunId: null, + chatSending: false, toolStreamById: new Map(), toolStreamOrder: [], toolStreamSyncTimer: null, @@ -195,9 +200,9 @@ describe("connectGateway", () => { expect(host.lastError).toBeNull(); secondClient.emitGap(20, 24); - expect(host.lastError).toBe( - "event gap detected (expected seq 20, got 24); refresh recommended", - ); + expect(gatewayClientInstances).toHaveLength(3); + expect(secondClient.stop).toHaveBeenCalledTimes(1); + expect(host.lastError).toBeNull(); }); it("ignores stale client onEvent callbacks after reconnect", () => { diff --git a/ui/src/ui/app-gateway.ts b/ui/src/ui/app-gateway.ts index ec1285e22c9..30097da1c14 100644 --- a/ui/src/ui/app-gateway.ts +++ b/ui/src/ui/app-gateway.ts @@ -264,8 +264,9 @@ export function connectGateway(host: GatewayHost) { if (host.client !== client) { return; } - host.lastError = `event gap detected (expected seq ${expected}, got ${received}); refresh recommended`; + host.lastError = `event gap detected (expected seq ${expected}, got ${received}); reconnecting`; host.lastErrorCode = null; + connectGateway(host); }, }); host.client = client;