* feat(whatsapp): subscribe to inbound reaction events
Subscribe to Baileys `messages.reaction` events and surface them as
system events for the agent session. Follows the same pattern used by
Signal and Slack: listen → parse → route → enqueueSystemEvent.
- Add `WebInboundReaction` type and `onReaction` callback to monitor
- Parse emoji, sender JID, target message ID from Baileys event
- Route via `resolveAgentRoute` and enqueue as system event
- Handle self-reaction attribution (reactionKey.fromMe → selfJid)
- Skip reaction removals (empty emoji) and status/broadcast JIDs
- Outer + inner try/catch for resilience against malformed events
- 13 unit tests (monitor-level + system event wiring)
- Changelog entry and docs updates
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* revert changelog entry to avoid merge conflicts
The inbound reaction feature is internal plumbing, not user-facing enough
to warrant a changelog entry that will conflict with active PRs.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat(whatsapp): handle reaction removals and improve sender detection
- Emit reaction events with isRemoval flag instead of skipping them
- Fall back to chatJid for DM sender when reaction.key is missing
- Add chatType and accountId to reaction logs for observability
- Update tests to reflect new removal handling behavior
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(whatsapp): gate reactions by DM/group access controls
Address Codex review - reactions now respect the same access controls
as messages (dmPolicy, allowlists, etc). Self-reactions bypass the
check since they're our own actions, not inbound events.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* chore: remove unused senderName field from reaction type
Baileys reaction events don't include push names, so this field
was dead interface pollution. (Cursor review)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(whatsapp): update reaction docs + suppress pairing for reactions
- Update docs: reaction removals now emit events with isRemoval=true
- Pass no-op sendMessage to access control to prevent pairing messages
being sent when unknown users react (pairing is for messages, not reactions)
(Cursor review)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Nick Sullivan <nick@technick.ai>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Self messages from the linked WhatsApp number bypass dmPolicy and allowFrom
checks automatically. Clarified that users don't need to add their own
number to the allowlist.
Self messages from the linked WhatsApp number bypass dmPolicy checks
entirely (via isSamePhone check in access-control.ts)...