Skip to content

fix(tools): resolve whatsapp phone numbers to lid format - #14738

Closed
ysnav wants to merge 4 commits into
NousResearch:mainfrom
ysnav:fix/whatsapp-lid-resolution
Closed

fix(tools): resolve whatsapp phone numbers to lid format#14738
ysnav wants to merge 4 commits into
NousResearch:mainfrom
ysnav:fix/whatsapp-lid-resolution

Conversation

@ysnav

@ysnav ysnav commented Apr 23, 2026

Copy link
Copy Markdown

What does this PR do?

This PR fixes a critical bug where AI-initiated tool calls and cron reminders failed to send messages over WhatsApp because they used bare phone numbers instead of the required Linked Identity Device (LID) formats.

Context: JIDs vs. LIDs

1 JID (Jabber ID): The traditional WhatsApp routing identifier. For direct messages, it looks like [phone_number]@s.whatsapp.net. For groups, it looks like [group_id]@g.us.

2 LID (Linked Identity Device): WhatsApp's modern, privacy-focused identifier format (e.g., 77214955630717@lid). The underlying Baileys bridge now strictly enforces this format for newer accounts and multi-device setups.

Why we can't fully ditch JIDs: We must continue supporting JIDs because group chats (@g.us) still rely entirely on the JID format. Additionally, we need to maintain legacy JID fallbacks (@s.whatsapp.net) for older accounts or edge cases where an LID isn't known yet.

The Bug

Incoming messages from WhatsApp to Hermes were working perfectly. This is because the local Baileys bridge natively handles incoming LIDs and automatically maps them to phone numbers for authorization (via allowlist.js).
However, the system broke on outgoing messages initiated by the AI (via tools) or by background Cron jobs. These systems only knew the user's bare phone number (e.g., +1234567890). When send_message_tool sent this bare phone number to the bridge, the bridge rejected it because it expects a fully qualified LID or JID.

The Fix

This PR implements a resolution pipeline that bridges the gap between the agent's knowledge (phone numbers) and the bridge's requirements (LIDs):

  1. Resolution Pipeline: Added _resolve_whatsapp_phone_to_jid to the send_message_tool. When a bare phone number is provided, it reads the local bridge session files (~/.hermes/whatsapp/session/lid-mapping-{phone}.json) created during the bridge handshake to gracefully convert the phone number to its proper LID.
  2. Smart Routing: Updated _parse_target_ref and _send_whatsapp to use this pipeline. If an already-valid JID is passed in (containing @lid, @s.whatsapp.net, or @g.us), it passes through untouched.
  3. Fallback Mechanism: If no mapping file exists for a phone number, it safely falls back to the legacy @s.whatsapp.net format.

Comprehensive unit tests were added in test_send_message_tool.py to ensure legacy JIDs, LIDs, group JIDs, and fallback mechanisms are all routed correctly.

Related Issue

Fixes #14486

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

  • tools/send_message_tool.py: Added _resolve_whatsapp_phone_to_jid and _ensure_whatsapp_jid helpers. Updated _parse_target_ref and _send_whatsapp routing to automatically resolve bare phone numbers and respect valid WhatsApp JIDs.
  • tests/tools/test_send_message_tool.py: Added test class TestWhatsAppPhoneToLidResolution and TestEnsureWhatsAppJid to cover mapping file lookups, legacy fallbacks, and standard JID passthrough.

How to Test

  1. Ensure your WhatsApp bridge session is active and mapping files exist in ~/.hermes/whatsapp/session/.
  2. Schedule a cron reminder using Hermes over WhatsApp with your bare phone number (e.g. "Remind me to wake up my friend at 6:39 PM").
  3. Wait for the cron job to trigger and observe that the delivery executes successfully instead of throwing a validation error.

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: macOS 14/15

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — or N/A
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A

For New Skills

  • This skill is broadly useful to most users (if bundled) — see Contributing Guide
  • SKILL.md follows the standard format (frontmatter, trigger conditions, steps, pitfalls)
  • No external dependencies that aren't already available (prefer stdlib, curl, existing Hermes tools)
  • I've tested the skill end-to-end: hermes --toolsets skills -q "Use the X skill to do Y"

Screenshots / Logs

Tested manually as well, by asking the agent to remind me at 18:39 via whatsapp, and it did remind me

@alt-glitch alt-glitch added type/bug Something isn't working P1 High — major feature broken, no workaround comp/gateway Gateway runner, session dispatch, delivery platform/whatsapp WhatsApp Business adapter labels Apr 23, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Related to #14486 (WhatsApp LID resolution) and PRs #3097, #3219 which address the same JID→LID migration gap from different angles.

@ysnav

ysnav commented Apr 23, 2026

Copy link
Copy Markdown
Author

Related to #14486 (WhatsApp LID resolution) and PRs #3097, #3219 which address the same JID→LID migration gap from different angles.

Thanks, I’ll go through PRs #3097 and #3219 and follow up.

@ysnav

ysnav commented Apr 25, 2026

Copy link
Copy Markdown
Author

Hi @alt-glitch,

I reviewed #3097 and #3219, and I think this PR is the most direct fix for #14486.

#14486 is primarily an outbound delivery bug: send_message is given a phone number, but the WhatsApp bridge now requires a valid WhatsApp identifier (@lid or compatible JID). This PR fixes that exact path by resolving bare phone numbers before the bridge send call.

By contrast:

  • #3219 improves inbound payload normalization for bridge -> gateway events, which helps authorization and sender identity consistency.
  • #3097 addresses the same general inbound authorization problem at the gateway layer.

Both are useful, but neither directly fixes the failing outbound send_message -> WhatsApp bridge /send flow described in #14486.

This PR also,

  • preserves already-valid WhatsApp targets (@lid, @s.whatsapp.net, @g.us)
  • falls back safely when no LID mapping exists
  • adds targeted tests for resolution and passthrough behavior

So my take is that #3219 and #3097 help with related inbound/auth concerns, while this PR is the best match for the issue being reported.

Adds a resolution pipeline `_resolve_whatsapp_phone_to_jid` to the
`send_message_tool`. Updates `_parse_target_ref` and `_send_whatsapp`
to use the resolution pipeline when bare phone numbers are provided.
Passes through already valid JIDs (ending with `@lid`, `@s.whatsapp.net`,
`@g.us`).

This resolves the issue where AI tool calls and cron reminders failed
to send WhatsApp messages because they passed bare phone numbers
instead of the newly enforced Linked Identity Device (LID) formats.
It checks the local bridge mapping files (`lid-mapping-{phone}.json`)
to gracefully convert phone numbers to LIDs.

Comprehensive unit tests were added in `test_send_message_tool.py`
testing legacy JIDs, LIDs, group JIDs, and fallback mechanisms.

Fixes: NousResearch#14486
@Isaac4real

Copy link
Copy Markdown

Hey @ysnav, thanks for taking a look into the issue I created!
looks good to me but could you please resolve the conflicts?

also, @alt-glitch, could you please take a look into this PR?

…suffixes

- Extracts phone-to-LID mapping logic from `tools/send_message_tool.py` into a shared `resolve_whatsapp_outbound_target` helper in `gateway/whatsapp_identity.py`.
- Restores explicit passthrough for known WhatsApp JIDs (`@lid`, `@s.whatsapp.net`, `@g.us`) in `_parse_target_ref()`.
- Strips unknown suffixes and falls back to resolving the prefix as a bare phone number.
- Adds comprehensive tests isolating `HERMES_HOME` to guarantee reproducible legacy fallback and suffix validation behavior.
- Normalizes `chatId` inside the live `WhatsAppAdapter.send()` to prevent initial cron delivery rejections.
@ysnav
ysnav force-pushed the fix/whatsapp-lid-resolution branch from b668f73 to 963d218 Compare April 27, 2026 14:01
@ysnav

ysnav commented Apr 27, 2026

Copy link
Copy Markdown
Author

Hey @ysnav, thanks for taking a look into the issue I created! looks good to me but could you please resolve the conflicts?

also, @alt-glitch, could you please take a look into this PR?

Thanks @Isaac4real, I have streamlined the code, and resolved the conflicts! I am awaiting review for this PR.

@ysnav

ysnav commented May 4, 2026

Copy link
Copy Markdown
Author

Hey @alt-glitch, can you please help review this PR?

@teknium1

Copy link
Copy Markdown
Contributor

Fixed on main via #50379 (salvage of #8639) — bare WhatsApp phone targets are now normalized to <digits>@s.whatsapp.net before the bridge call at every outbound send site.

@sgaofen's #8639 was the earliest of this cluster, so it was used as the base and credited first; thanks @ysnav for independently catching and fixing the same bug. Closing as duplicate.

@teknium1 teknium1 closed this Jun 21, 2026
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 P1 High — major feature broken, no workaround platform/whatsapp WhatsApp Business adapter type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: WhatsApp bridge fails to resolve contacts from allowlist when using phone numbers

4 participants