@@ -64,6 +64,50 @@ function describeReplyContext(message: IMessagePayload): IMessageReplyContext |
6464 return { body, id, sender } ;
6565}
6666
67+ function resolveInboundEchoMessageIds ( message : IMessagePayload ) : string [ ] {
68+ const values = [
69+ message . id != null ? String ( message . id ) : undefined ,
70+ normalizeReplyField ( message . guid ) ,
71+ ] ;
72+ const ids : string [ ] = [ ] ;
73+ for ( const value of values ) {
74+ if ( ! value || ids . includes ( value ) ) {
75+ continue ;
76+ }
77+ ids . push ( value ) ;
78+ }
79+ return ids ;
80+ }
81+
82+ function hasIMessageEchoMatch ( params : {
83+ echoCache : {
84+ has : (
85+ scope : string ,
86+ lookup : { text ?: string ; messageId ?: string } ,
87+ skipIdShortCircuit ?: boolean ,
88+ ) => boolean ;
89+ } ;
90+ scope : string ;
91+ text ?: string ;
92+ messageIds : string [ ] ;
93+ skipIdShortCircuit ?: boolean ;
94+ } ) : boolean {
95+ for ( const messageId of params . messageIds ) {
96+ if ( params . echoCache . has ( params . scope , { messageId } ) ) {
97+ return true ;
98+ }
99+ }
100+ const fallbackMessageId = params . messageIds [ 0 ] ;
101+ if ( ! params . text && ! fallbackMessageId ) {
102+ return false ;
103+ }
104+ return params . echoCache . has (
105+ params . scope ,
106+ { text : params . text , messageId : fallbackMessageId } ,
107+ params . skipIdShortCircuit ,
108+ ) ;
109+ }
110+
67111export type IMessageInboundDispatchDecision = {
68112 kind : "dispatch" ;
69113 isGroup : boolean ;
@@ -168,9 +212,9 @@ export function resolveIMessageInboundDecision(params: {
168212 // When true, the selfChatCache.has() check below must be skipped — we just
169213 // called remember() and would immediately match our own entry.
170214 let skipSelfChatHasCheck = false ;
171- const inboundMessageId =
172- normalizeReplyField ( params . message . guid ) ??
173- ( params . message . id != null ? String ( params . message . id ) : undefined ) ;
215+ const inboundMessageIds = resolveInboundEchoMessageIds ( params . message ) ;
216+ const inboundMessageId = inboundMessageIds [ 0 ] ;
217+ const hasInboundGuid = Boolean ( normalizeReplyField ( params . message . guid ) ) ;
174218
175219 if ( params . message . is_from_me ) {
176220 // Always cache in selfChatCache so the upcoming is_from_me=false reflection
@@ -190,11 +234,13 @@ export function resolveIMessageInboundDecision(params: {
190234 if (
191235 params . echoCache &&
192236 ( bodyText || inboundMessageId ) &&
193- params . echoCache . has (
194- echoScope ,
195- { text : bodyText || undefined , messageId : inboundMessageId } ,
196- ! normalizeReplyField ( params . message . guid ) ,
197- )
237+ hasIMessageEchoMatch ( {
238+ echoCache : params . echoCache ,
239+ scope : echoScope ,
240+ text : bodyText || undefined ,
241+ messageIds : inboundMessageIds ,
242+ skipIdShortCircuit : ! hasInboundGuid ,
243+ } )
198244 ) {
199245 return { kind : "drop" , reason : "agent echo in self-chat" } ;
200246 }
@@ -305,9 +351,11 @@ export function resolveIMessageInboundDecision(params: {
305351 sender,
306352 } ) ;
307353 if (
308- params . echoCache . has ( echoScope , {
354+ hasIMessageEchoMatch ( {
355+ echoCache : params . echoCache ,
356+ scope : echoScope ,
309357 text : bodyText || undefined ,
310- messageId : inboundMessageId ,
358+ messageIds : inboundMessageIds ,
311359 } )
312360 ) {
313361 params . logVerbose ?.(
0 commit comments