---
name: whatsapp-find-missing-contact
description: Recover a WhatsApp contact or group that a normal lookup could NOT find. Trigger ONLY after search_contacts / list_chats / get_direct_chat_by_contact already returned nothing (or the wrong person) for a name the user is sure exists — this is the fallback ladder, not the first-line lookup. Requires the WhatsApp connector tools.
---

# WhatsApp find-missing-contact (when the normal lookup fails)

> Not the first-line lookup. Use the `*_contacts` / `list_chats` tools directly
> for a normal "find X" request. Invoke this skill only once that has already
> failed — it exists to recover from a missed search, not to replace it.

`search_contacts` failing does **not** mean the contact is absent. It is a
narrow query, and WhatsApp's local data has known gaps. This skill is the
fallback ladder: run the cheap retries first, escalate, and only declare "not
found" after the ladder is exhausted.

## Why the first search misses (read once)

- **`search_contacts` only matches the `chats` table** (`name` OR `jid`), and the
  stored `name` is **often a phone number, not the display name** — WhatsApp
  doesn't always backfill contact names. A name search then finds nothing even
  though the person is there under a number.
- **It excludes groups entirely** (`@g.us` filtered out). Never use it to find a
  group — use `list_chats`.
- **It needs an existing chat row.** A contact you've never exchanged a message
  with, or a **brand-new group with zero messages**, has no row yet and cannot be
  found by any search — only by browsing or after one message exists.
- **Real names** (first/full/push name, native-script) live in a separate
  credential DB the tools don't join — so the *value* you'd search for may simply
  not be in the searchable column.

## The ladder — stop as soon as a step resolves the contact

Run top to bottom. Each step is one or two tool calls; don't skip to asking the
user until the no-input steps are done.

### 1. Name variants (no user input)
Re-run `search_contacts` with looser terms — the LIKE match is substring but
case- and spelling-sensitive:
- first name only, then last name only (not the full "First Last").
- a nickname / shortened form.
- drop or change accents/punctuation; try a 3–4 letter unique fragment.

### 2. Search by phone number (no user input, if you have any digits)
- `search_contacts` with the number (it matches `jid` too).
- `get_direct_chat_by_contact(sender_phone_number=…)` — resolves the DM directly
  from a number even when the name column is blank/numeric.
- A partial number works as a fragment in `search_contacts`.

### 3. Is it a group? (no user input)
`search_contacts` can't see groups. Use:
- `list_chats(query="<name>")` — searches groups **and** DMs by name/jid.
- If still nothing and the user said it's a **new group**: it may have **no
  messages yet → no chat row**. Ask the user to send/receive one message in it
  (or to open it), then retry — there's no other way to surface an empty group.

### 4. Other language / script (needs user input)
Names are stored as the contact saved them. An English query won't match a
contact stored in Hebrew/Arabic/Cyrillic/etc.
- Ask: *"How is the name written in their language / on their profile?"*
- Retry `search_contacts` and `list_chats` with the native-script spelling and
  with a transliteration.

### 5. Global content search → reverse-resolve (needs user input)
The strongest fallback when the name is unknown or unstored. Find the person via
something they *said*, then read their identity off the message.
- Ask: *"Give me a distinctive word or phrase from a message with them."*
- `list_messages(query="<phrase>", limit=20, include_context=True)`.
- From each hit, take `sender` (the number/jid) and `chat_jid`, then:
  - `get_chat(chat_jid)` for the chat name, or
  - `get_direct_chat_by_contact(sender)` / `search_contacts(sender)` to name them.
- Confirm the right hit with the user before acting on it.

### 6. Browse recents (last resort)
`list_chats(sort_by="last_active", limit=50)` and skim with the user — useful when
the contact is stored under a number or an unexpected spelling and nothing above
matched.

## Output

- When resolved: report the resolved **display name + chat_jid** (never the phone
  number — see privacy) and which step found it, so the user learns the gap.
- When exhausted: say plainly which steps you ran and that the contact has no
  searchable local record — most likely never messaged, an empty new group, or
  stored only under a number. Offer step 5 (content search) if not yet tried.

## Privacy (non-negotiable)

- Never print the user's own phone number, and never surface a contact's full
  number in chat — use the display name or a masked form.
- Don't dump message bodies while reverse-resolving; quote only the minimum
  needed to confirm the match with the user.
