From a84e43dadc24b7bdb42f47280702ae7aceb8cd0b Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Sun, 22 Jun 2025 20:21:03 +0200 Subject: [PATCH] fix: Prevent duplicate broadcasts in StreamWatcher MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add deduplication logic to prevent the same line from being broadcast twice when both direct notification and file watcher fire. Uses a simple hash and 50ms time window to detect duplicates. This fixes the double input issue in server-created sessions. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- web/src/server/services/stream-watcher.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/web/src/server/services/stream-watcher.ts b/web/src/server/services/stream-watcher.ts index 77ee4925..055d0866 100644 --- a/web/src/server/services/stream-watcher.ts +++ b/web/src/server/services/stream-watcher.ts @@ -13,6 +13,8 @@ interface WatcherInfo { lastOffset: number; lineBuffer: string; notificationListener?: (update: { sessionId: string; data: string; timestamp: number }) => void; + lastBroadcastTime: number; + recentBroadcasts: Set; } export class StreamWatcher { @@ -40,6 +42,8 @@ export class StreamWatcher { clients: new Set(), lastOffset: 0, lineBuffer: '', + lastBroadcastTime: 0, + recentBroadcasts: new Set(), }; this.activeWatchers.set(sessionId, watcherInfo); @@ -269,6 +273,23 @@ export class StreamWatcher { * Broadcast a line to all clients */ private broadcastLine(sessionId: string, line: string, watcherInfo: WatcherInfo): void { + // Deduplication: check if we've broadcast this line very recently + const now = Date.now(); + const lineHash = `${line.substring(0, 100)}_${line.length}`; // Simple hash + + if (watcherInfo.recentBroadcasts.has(lineHash) && now - watcherInfo.lastBroadcastTime < 50) { + // Skip duplicate within 50ms window + return; + } + + // Clean up old broadcasts + if (now - watcherInfo.lastBroadcastTime > 100) { + watcherInfo.recentBroadcasts.clear(); + } + + watcherInfo.recentBroadcasts.add(lineHash); + watcherInfo.lastBroadcastTime = now; + let eventData: string | null = null; try {