#19947: fix(imessage): hash-based echo detection and is_from_me early exit
channel: imessage
size: M
## Summary
Fixes #12873. When the iMessage bridge delivers the bot’s own messages back as inbound with `is_from_me` null or missing, the bot treated them as user messages and produced echo/loops. This PR fixes that.
**Cause:** Previously we (1) dropped “from me” only when `is_from_me` was truthy, and (2) used a 5-second TTL text cache to suppress “recently sent bot message” echoes. The bridge sometimes sends bot messages with `is_from_me: null`, and when LLM inference exceeds 5 seconds the TTL expires so echoes get through — both mechanisms failed in those cases.
**What this branch does:**
Echo/duplicate handling now uses **content-hash-based detection** and **`is_from_me` early exit** together.
- **Hash-based echo detection:** When the bot sends a message we store `SHA-256(scope + text)`. On inbound, if the same hash exists we treat it as a bot echo and drop it. No TTL; a FIFO cap of 200 entries and one-shot removal on match reduce false positives and bound memory.
- **`is_from_me` early exit:** If the bridge reports `is_from_me: true`, we drop immediately so the same user message is not processed twice.
- **Shared scope:** `echo-scope.ts` ensures delivery and inbound use the same scope key so hashes align.
So: bridge-reported “from me” → early exit; when `is_from_me` is null/missing, bot echoes are caught by hash matching.
**Code changes:**
`SentMessageCache` is extracted into its own module and now stores/checks hashes with a bounded FIFO; `monitor-provider` uses it. In `inbound-processing` we early-return when `is_from_me` is set, then rely on the hash-based echo cache for everything else. On delivery we build scope via shared `buildIMessageEchoScope` and only store the hash in the cache. Review follow-ups: removed the stale 5-second TTL comment and the redundant pre-chunk `remember()` call.
## Test plan
- [x] Unit tests for `SentMessageCache` (hash storage, echo match, FIFO eviction, one-shot removal)
- [x] Unit tests for `echo-scope` (DM/group scope identical between delivery and inbound)
- [x] `deliverReplies`-related tests
- [ ] Manual: verify no echo loop with slow inference (e.g. local Ollama >5s)
Most Similar PRs
#2799: fix(imessage): prevent self-chat and outbound echo loops (#2585)
by Tfh-Yqf · 2026-01-27
65.6%
#21890: fix(whatsapp): propagate fromMe flag through DM message pipeline
by mactsk · 2026-02-20
63.0%
#17326: fix(whatsapp): group composing indicator, echo prevention, and pres...
by globalcaos · 2026-02-15
62.7%
#16186: fix(slack): thread history on subsequent turns, inbound meta contex...
by markshields-tl · 2026-02-14
62.6%
#15240: fix(bluebubbles): URL dropped when sent in same iMessage bubble as ...
by yinghaosang · 2026-02-13
62.2%
#16655: fix(whatsapp): resolve reply-to sender E.164 for LID JIDs (have bot...
by mascarenhas · 2026-02-15
62.2%
#19489: fix(voice-call): add echo suppression for TTS playback
by kalichkin · 2026-02-17
62.2%
#7719: fix(slack): thread replies with @mentions dropped in requireMention...
by SocialNerd42069 · 2026-02-03
61.7%
#22168: fix(imessage): resolve group sessions without chat identifiers
by AIflow-Labs · 2026-02-20
61.3%
#20152: fix(slack): allow app_mention events to bypass dedup cache
by nova-openclaw-cgk · 2026-02-18
60.9%