fix(imessage): drop ambiguous reflected self-chat echoes

This commit is contained in:
Ayaan Zaidi
2026-04-10 13:42:02 +05:30
parent c3d3cf23bc
commit e3e2a19ab7
3 changed files with 11 additions and 2 deletions

View File

@@ -76,6 +76,7 @@ Docs: https://docs.openclaw.ai
- Gateway/thread routing: preserve Slack, Telegram, and Mattermost thread-child delivery targets so bound subagent completion messages land in the originating thread instead of top-level channels. (#54840) Thanks @yzzymt.
- ACP/stream relay: pass parent delivery context to ACP stream relay system events so `streamTo="parent"` updates route to the correct thread or topic instead of falling back to the main DM. (#57056) Thanks @pingren.
- Agents/sessions: preserve announce `threadId` when `sessions.list` fallback rehydrates agent-to-agent announce targets so final announce messages stay in the originating thread/topic. (#63506) Thanks @SnowSky1.
- iMessage/self-chat: remember ambiguous `sender === chat_identifier` outbound rows with missing `destination_caller_id` in self-chat dedupe state so the later reflected inbound copy still drops instead of re-entering inbound handling when the echo cache misses. Thanks @neeravmakwana.
## 2026.4.9

View File

@@ -217,12 +217,20 @@ export function resolveIMessageInboundDecision(params: {
chatIdentifierNormalized != null &&
senderNormalized === chatIdentifierNormalized &&
matchesSelfChatDestination;
const isAmbiguousSelfThread =
!isGroup &&
chatIdentifierNormalized != null &&
senderNormalized === chatIdentifierNormalized &&
destinationCallerIdNormalized == null;
let skipSelfChatHasCheck = false;
const inboundMessageIds = resolveInboundEchoMessageIds(params.message);
const inboundMessageId = inboundMessageIds[0];
const hasInboundGuid = Boolean(normalizeReplyField(params.message.guid));
if (params.message.is_from_me) {
if (isAmbiguousSelfThread) {
params.selfChatCache?.remember(selfChatLookup);
}
if (isSelfChat) {
params.selfChatCache?.remember(selfChatLookup);
const echoScope = buildIMessageEchoScope({

View File

@@ -630,7 +630,7 @@ describe("self-chat is_from_me=true handling (Bruce Phase 2 fix)", () => {
expect(decision).toEqual({ kind: "drop", reason: "from me" });
});
it("does not drop reflected inbound when destination_caller_id is absent (#63980)", () => {
it("drops reflected inbound when destination_caller_id is absent (#63980)", () => {
vi.useFakeTimers();
vi.setSystemTime(new Date("2026-03-24T12:00:00Z"));
@@ -674,7 +674,7 @@ describe("self-chat is_from_me=true handling (Bruce Phase 2 fix)", () => {
}),
);
expect(reflection.kind).toBe("dispatch");
expect(reflection).toEqual({ kind: "drop", reason: "self-chat echo" });
});
it("normal DM is_from_me=true is still dropped (regression test)", () => {