@@ -43,6 +43,7 @@ SyntaxHighlighter.registerLanguage('dockerfile', docker);
4343import type { Message , Agent , Attachment } from '../types' ;
4444import { MessageStatusIndicator } from './MessageStatusIndicator' ;
4545import { ThinkingIndicator } from './ThinkingIndicator' ;
46+ import { deduplicateBroadcasts } from './hooks/useBroadcastDedup' ;
4647
4748// Provider icons and colors matching landing page
4849const PROVIDER_CONFIG : Record < string , { icon : string ; color : string } > = {
@@ -127,7 +128,7 @@ export function MessageList({
127128 const isScrollingRef = useRef ( false ) ;
128129
129130 // Filter messages for current channel or current thread
130- const filteredMessages = messages . filter ( ( msg ) => {
131+ const channelFilteredMessages = messages . filter ( ( msg ) => {
131132 // When a thread is selected, show messages related to that thread
132133 if ( currentThread ) {
133134 // Show the original message (id matches thread) or replies (thread field matches)
@@ -148,6 +149,14 @@ export function MessageList({
148149 return msg . from === currentChannel || msg . to === currentChannel ;
149150 } ) ;
150151
152+ // Deduplicate broadcast messages in #general channel
153+ // When a broadcast is sent to '*', the backend delivers it to each recipient separately,
154+ // causing the same message to appear multiple times. Deduplication removes duplicates
155+ // by grouping broadcasts with the same sender, content, and timestamp.
156+ const filteredMessages = currentChannel === 'general'
157+ ? deduplicateBroadcasts ( channelFilteredMessages )
158+ : channelFilteredMessages ;
159+
151160 // Populate latestMessageToAgent with the latest message from current user to each agent
152161 // Iterate in order (oldest to newest) so the last one wins
153162 for ( const msg of filteredMessages ) {
0 commit comments