fix(imessage): require destination_caller_id for self-chat classification (#63980)#63989
Conversation
792ba45 to
5438660
Compare
Greptile SummaryThis PR fixes a self-chat misclassification in the iMessage monitor: previously, a missing The one-line logic inversion ( Confidence Score: 5/5Safe to merge — targeted one-line predicate fix with complete test coverage and no regressions. All findings are at most P2. The logic inversion correctly fixes the null-treated-as-match bug. Existing self-chat dispatch tests are updated with explicit destination_caller_id, the renamed test expectation matches the new behavior, and a new regression test locks in the absent-destination drop path. The known trade-off (true self-chat rows without destination_caller_id now drop as from me) is explicitly acknowledged in the PR risk section and is the correct conservative default. No files require special attention. Reviews (1): Last reviewed commit: "fix(imessage): require destination_calle..." | Re-trigger Greptile |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 54386603dc
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Follow-up: AI review (Codex P1) + test alignmentCodex concern (P1 — self-chat message loss when
|
🔒 Aisle Security AnalysisWe found 1 potential security issue(s) in this PR:
1. 🟡 Inbound iMessage reflection may be dispatched when destination_caller_id is missing, enabling self-echo loops
Description
As a result:
This creates a self-echo / message-loop risk where the agent processes its own outputs as inputs, potentially triggering unintended command execution or recursive reply amplification. Vulnerable logic: // destination_caller_id must be present to be considered self-chat
const matchesSelfChatDestination =
destinationCallerIdNormalized != null && destinationCallerIdNormalized === senderNormalized;
...
if (params.message.is_from_me) {
if (isSelfChat) {
params.selfChatCache?.remember(selfChatLookup);
...
} else {
return { kind: "drop", reason: "from me" };
}
}
...
const selfChatHit = skipSelfChatHasCheck
? false
: params.selfChatCache?.has({ ...selfChatLookup, text: bodyText });The accompanying tests explicitly assert this behavior (reflected inbound is dispatched when RecommendationPrevent reflected duplicates from being dispatched even when Options (pick one consistent with desired product behavior):
Example approach (conceptual): // record outbound rows even if not self-chat
if (params.message.is_from_me) {
params.selfChatCache?.remember(selfChatLookup);
return isSelfChat ? continueDecision : { kind: "drop", reason: "from me" };
}
// drop reflections for any remembered outbound row
if (params.selfChatCache?.has({ ...selfChatLookup, text: bodyText })) {
return { kind: "drop", reason: "outbound reflection" };
}Also consider persistence across restarts (or a longer TTL) if the loop risk is unacceptable when Analyzed PR: #63989 at commit Last updated on: 2026-04-10T08:15:13Z |
d6034c2 to
3bca20a
Compare
There was a problem hiding this comment.
Verified the missing-destination_caller_id DM/self-chat misclassification and the reflected-copy loss path.
Maintainer follow-up: scoped selfChatCache.remember() to confirmed self-chat and updated the self-chat regression fixtures after rebasing onto main.
Local gate: pnpm test:extension imessage.
|
Landed on main. Thanks @neeravmakwana. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d6034c23ee
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const matchesSelfChatDestination = | ||
| destinationCallerIdNormalized == null || destinationCallerIdNormalized === senderNormalized; | ||
| destinationCallerIdNormalized != null && destinationCallerIdNormalized === senderNormalized; | ||
| const isSelfChat = |
There was a problem hiding this comment.
Preserve self-chat delivery when destination ID is absent
This classification now requires destination_caller_id to be present and equal to sender, so true self-chat rows that omit destination are treated as non-self-chat and then dropped via the is_from_me path. Fresh evidence in this commit is that selfChatCache.remember(...) was moved inside if (isSelfChat), so there is no longer a cache fallback for these rows; delivery now depends on an additional is_from_me=false reflection copy arriving, and environments that emit only the from-me row will lose user self-chat messages.
Useful? React with 👍 / 👎.
…) (thanks @neeravmakwana) * fix(imessage): require destination_caller_id for self-chat classification (#63980) Made-with: Cursor * fix(imessage): scope self-chat cache to self-chat --------- Co-authored-by: Ayaan Zaidi <[email protected]>
…claw#63989) (thanks @neeravmakwana) * fix(imessage): require destination_caller_id for self-chat classification (openclaw#63980) Made-with: Cursor * fix(imessage): scope self-chat cache to self-chat --------- Co-authored-by: Ayaan Zaidi <[email protected]>
…claw#63989) (thanks @neeravmakwana) * fix(imessage): require destination_caller_id for self-chat classification (openclaw#63980) Made-with: Cursor * fix(imessage): scope self-chat cache to self-chat --------- Co-authored-by: Ayaan Zaidi <[email protected]>
…claw#63989) (thanks @neeravmakwana) * fix(imessage): require destination_caller_id for self-chat classification (openclaw#63980) Made-with: Cursor * fix(imessage): scope self-chat cache to self-chat --------- Co-authored-by: Ayaan Zaidi <[email protected]>
…claw#63989) (thanks @neeravmakwana) * fix(imessage): require destination_caller_id for self-chat classification (openclaw#63980) Made-with: Cursor * fix(imessage): scope self-chat cache to self-chat --------- Co-authored-by: Ayaan Zaidi <[email protected]>
…claw#63989) (thanks @neeravmakwana) * fix(imessage): require destination_caller_id for self-chat classification (openclaw#63980) Made-with: Cursor * fix(imessage): scope self-chat cache to self-chat --------- Co-authored-by: Ayaan Zaidi <[email protected]>
Summary
senderandchat_identifiermatch for both directions. Whendestination_caller_idwas missing, the monitor treated the thread as self-chat (matchesSelfChatDestinationwas true because a missing destination was treated as a match). Outboundis_from_memessages then skipped the normalfrom medrop and could loop when echo cache missed.#63980).destination_caller_idto be present and normalized-equal tosender. Tests that model true self-chat include that field; new regression tests cover absent/blank destination withsender === chat_identifier.destination_caller_iddiffers fromsender(normal DM) remain as before.Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
Root Cause (if applicable)
matchesSelfChatDestinationuseddestinationCallerIdNormalized == null || destinationCallerIdNormalized === senderNormalized, so a missing destination behaved like a confirmed self-chat destination match wheneversender === chat_identifier.destination_caller_idwere indistinguishable from self-chat on those two fields alone.chat.dbrow shapes vary; some DMs omit destination in the payload OpenClaw sees.Regression Test Plan (if applicable)
extensions/imessage/src/monitor/self-chat-dedupe.test.tsis_from_me+sender === chat_identifier+ missing or blankdestination_caller_iddrops asfrom me; true self-chat still dispatches whendestination_caller_idmatchessender.chat.db.uses destination_caller_id to avoid DM self-chat false positives(populated destination differing from sender).User-visible / Behavior Changes
destination_caller_idare no longer classified as self-chat solely fromsender === chat_identifier. True self-chat rows should includedestination_caller_idmatching the sender (as in existing supported cases).Diagram (if applicable)
N/A
Security Impact (required)
Yes, explain risk + mitigation:Repro + Verification
Environment
Steps
pnpm test:extension imessagepnpm checkExpected
Actual
pnpm checkgreen.Evidence
Human Verification (required)
#63980.destination_caller_id; existing DM tests with differingdestination_caller_id; self-chat fixtures updated with explicit destination matching sender.chat.db/ imsg on hardware.Review Conversations
Compatibility / Migration
Risks and Mitigations
destination_caller_idcould be treated as non-self-chat; outboundis_from_mewould drop asfrom meinstead of taking the self-chat branch.destination_caller_idmatching sender.Made with Cursor