Skip to content

feat: add WhatsApp quote and status primitives - #21977

Closed
aldoeliacim wants to merge 1 commit into
NousResearch:mainfrom
aldoeliacim:feat/whatsapp-status-reply-primitives
Closed

feat: add WhatsApp quote and status primitives#21977
aldoeliacim wants to merge 1 commit into
NousResearch:mainfrom
aldoeliacim:feat/whatsapp-status-reply-primitives

Conversation

@aldoeliacim

@aldoeliacim aldoeliacim commented May 8, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Adds WhatsApp quote/reaction/status primitives that Baileys supports natively but Hermes was paving over with plain-text follow-ups.

  • Adds a bounded WhatsApp bridge WAMessage cache so Baileys can send native quoted replies instead of plain-text follow-ups.
  • Propagates incoming quote metadata (quotedMessageId, quotedText, quotedRemoteJid) into Hermes MessageEvent reply context.
  • Wires media replies through /send-media and adds bridge primitives for message reactions, private status replies, status reactions, and explicit-recipient text status posts.
  • Keeps status ingestion disabled by default; statuses are only admitted when platforms.whatsapp.extra.statuses.enabled: true and ingest: true.
  • Adds whatsapp_action, a WhatsApp-specific messaging tool for owner/user-requested advanced actions: react_message, status_reply, status_react, and post_text_status.
  • Adds Node bridge tests and Python tool/adapter tests.

Design notes

  • The advanced status/reaction primitives live behind bridge/adapter methods and a WhatsApp-specific tool rather than broadening generic send_message; this keeps WhatsApp-specific semantics explicit.
  • whatsapp_action includes dry_run=true so agents can inspect the exact bridge endpoint/payload before performing external side effects.
  • Status posting requires a non-empty explicit status_jid_list; there is intentionally no "all contacts" fallback.
  • Native quotes degrade gracefully: if replyTo is not in the bridge cache, the bridge sends the message without a quote and reports quoted: false.
  • Bridge imports are now side-effect-light so node:test can import helper functions without starting an HTTP server or socket.

Operational follow-up after merge

  • Real-device smoke test for status replies/reactions; Baileys status support is known to be flaky upstream and should be validated against an actual WhatsApp session before enabling any automation.

Related Issue

No related issue — surfaced from production WhatsApp use where plain-text "@reply" fallbacks felt wrong vs the platform's native quote/reaction UX.

Fixes #

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

  • scripts/whatsapp-bridge/bridge.js — WAMessage cache; new /send-media-reply, /react, /status-reply, /status-react, /post-text-status endpoints; side-effect-light import surface for tests.
  • scripts/whatsapp-bridge/bridge.test.mjs — Node test suite for the new primitives.
  • gateway/platforms/whatsapp.py — quote metadata propagation; adapter methods for reactions/status/replies.
  • tools/whatsapp_action_tool.py — new whatsapp_action tool: react_message, status_reply, status_react, post_text_status, with dry_run.
  • tests/tools/test_whatsapp_action_tool.py, tests/gateway/test_whatsapp_*.py — coverage for tool + adapter wiring.

How to Test

  1. ./venv/bin/python -m pytest tests/tools/test_whatsapp_action_tool.py tests/gateway/test_whatsapp_*.py tests/gateway/test_platform_base.py -q
    — 188 passing on current rebase.
  2. cd scripts/whatsapp-bridge && npm test -- --test-reporter=spec for the Node bridge primitives.
  3. node --check scripts/whatsapp-bridge/bridge.js and ./venv/bin/python -m py_compile tools/whatsapp_action_tool.py gateway/platforms/whatsapp.py for syntax sanity.
  4. Real-device smoke test (post-merge): use the whatsapp_action tool with dry_run=true first to inspect the bridge payload, then run with dry_run=false against a paired WhatsApp session.

Checklist

  • Tests added/updated and passing locally (188 pass)
  • Rebased on origin/main (2026-05-25; one trivial conflict in whatsapp.py was an additive merge — kept upstream's new pre-flight pairing check plus this PR's bridge-found log line)
  • Follows existing code conventions for gateway/platforms/* adapters and tools/* modules
  • Documentation updated where needed (design notes inline + tool docstrings)
  • Conventional commit messages

Notes for reviewers

  • The breaking-change risk is contained to the WhatsApp surface; whatsapp_action is a new tool, whatsapp.py additions are backward compatible (existing callers see the same methods plus new optional ones).
  • Status ingest stays off by default. The opt-in lives behind two flags (enabled + ingest) precisely because Baileys status support is fragile and shouldn't fail-open into surprise behavior.

@aldoeliacim
aldoeliacim marked this pull request as ready for review May 8, 2026 17:13
Copilot AI review requested due to automatic review settings May 8, 2026 17:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@alt-glitch alt-glitch added type/feature New feature or request P2 Medium — degraded but workaround exists platform/whatsapp WhatsApp Business adapter comp/gateway Gateway runner, session dispatch, delivery labels May 11, 2026
@aldoeliacim
aldoeliacim force-pushed the feat/whatsapp-status-reply-primitives branch from 828e39d to 16cfe6e Compare May 15, 2026 15:09
@aldoeliacim
aldoeliacim requested a review from a team May 15, 2026 15:09

@austinpickett austinpickett left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please fix merge conflicts and use .github/PULL_REQUEST_TEMPLATE.md

@aldoeliacim
aldoeliacim force-pushed the feat/whatsapp-status-reply-primitives branch from 16cfe6e to 88322e2 Compare May 25, 2026 09:09
@aldoeliacim
aldoeliacim requested a review from austinpickett May 25, 2026 09:38
@aldoeliacim
aldoeliacim force-pushed the feat/whatsapp-status-reply-primitives branch 3 times, most recently from 234cc04 to 28a2131 Compare June 2, 2026 15:08
@aldoeliacim

Copy link
Copy Markdown
Contributor Author

@austinpickett both points from your review should be addressed now:

  1. Merge conflicts — resolved. The branch is freshly rebased onto current origin/main (force-pushed above) and now merges cleanly with zero conflicts. Locally tested: 60 passed across the WhatsApp formatting/gating/action-tool suites.
  2. PR template — the description already follows .github/PULL_REQUEST_TEMPLATE.md (What/Related Issue/Type/Changes Made/How to Test/Checklist sections are all filled in).

Would appreciate a re-review when you have a moment. Thanks!

@aldoeliacim
aldoeliacim force-pushed the feat/whatsapp-status-reply-primitives branch 3 times, most recently from 1a4a4aa to 9255371 Compare June 11, 2026 06:21
@aldoeliacim
aldoeliacim force-pushed the feat/whatsapp-status-reply-primitives branch from 9255371 to 0350ef3 Compare June 17, 2026 16:52
@aldoeliacim

Copy link
Copy Markdown
Contributor Author

Acknowledged — this branch has drifted into a multi-commit stack that overlaps several other open PRs and needs a clean rescope onto current origin/main (rebuilding with only the net-new WhatsApp quote/status/reaction primitives, dropping the commits that have since landed or that belong to sibling PRs). The straight rebase hits a design conflict in gateway/platforms/base.py / whatsapp_common.py where upstream refactored the functions this PR edits in place.

I'm handling that rescope as its own focused pass rather than force a hand-resolution here, and will re-push against the PR template once it's a single coherent commit. Leaving the branch as-is in the meantime so no half-resolved state lands.

@aldoeliacim

Copy link
Copy Markdown
Contributor Author

Heads-up for reviewers: the reaction portion of this PR (the bounded TTL'd messageStore + /react endpoint + unified adapter contract) has been split out and reconciled into #50670 (stacked on #50661), which is green and reviewable as a focused unit. This PR's remaining unique surface is the status primitives (status-reply / status-react / post-status) and quote threading. It still predates the platform-plugin migration (#41112) and needs a refresh onto current main; I'll rebase the status-only scope separately.

@aldoeliacim
aldoeliacim force-pushed the feat/whatsapp-status-reply-primitives branch 4 times, most recently from 212f83c to 7345125 Compare July 1, 2026 20:40
@aldoeliacim
aldoeliacim force-pushed the feat/whatsapp-status-reply-primitives branch from 7345125 to c3a2c72 Compare July 3, 2026 00:16
@aldoeliacim
aldoeliacim force-pushed the feat/whatsapp-status-reply-primitives branch from c3a2c72 to 172498e Compare July 4, 2026 19:58
Add outbound WhatsApp primitives that the existing send path doesn't
cover: emoji reactions to cached messages, private replies to status
updates, reactions to statuses, and posting text statuses to an explicit
recipient list. Exposed through a new `whatsapp_action` tool that is
service-gated (check_fn=_check_whatsapp_action) so it only enters the
schema when WhatsApp is a configured platform — it never ships on calls
for users without WhatsApp.

Bridge (Node):
- Bounded, TTL'd WAMessage store (messageStore) so Baileys can resolve
  the original message object required for native quotes and reactions.
- New endpoints: POST /react, /status-reply, /status-react, /post-status.
- extractMessageText / extractQuoteContext helpers; thread quoted-send
  options through /send-media, honoring an explicit per-request upload
  timeout when provided and falling back to sendWithTimeout's default.

Adapter (Python):
- _is_status_broadcast_id helper (in WhatsAppBehaviorMixin) + status-ingest
  opt-in in _should_process_message, reconciled with main's broadcast-list
  gate (status updates dropped unless statuses.enabled && statuses.ingest).
- _post_bridge_action + react_to_message / reply_to_status /
  react_to_status / post_text_status on the Baileys adapter.
- Thread reply_to through _send_media_to_bridge and the send_* wrappers.
- Surface quoted-message context (reply_to_message_id / reply_to_text)
  on inbound MessageEvent.

Tests: bridge.test.mjs (message store, quote options, status resolution),
test_whatsapp_action_tool.py, plus formatting/group-gating additions.

Re-ported onto current main after the adapter was relocated to
plugins/platforms/whatsapp/adapter.py and gating moved into
WhatsAppBehaviorMixin; the prior 12-commit stack of stale cherry-picks is
collapsed to this single feature commit.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists platform/whatsapp WhatsApp Business adapter type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants