Two bugs fixed compared to prior implementation:
1. history.ts — normalize userJid LID→PN
pastParticipants[].userJid may arrive as a LID identifier (e.g. "46802258641027@lid").
The consumer expects a phone number. After building the lidPnMap from conversations
and phoneNumberToLidMappings, each userJid is resolved to its PN equivalent.
If the mapping is not available the original value is preserved (no data loss).
2. event-buffer.ts — deduplicate by groupJid + userJid across chunks
Multiple HistorySync chunks can contain the same group. The previous approach
concatenated blindly, producing duplicate entries. Now a keyed map
{ [groupJid]: IPastParticipant[] } is used so each group appears once and
each participant within a group appears at most once.
Types updated (Events.ts):
- messaging-history.set event includes pastParticipants?: IPastParticipants[]
- BufferedEventData.historySets.pastParticipants typed as keyed map
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add interactive message detection helpers (getButtonType, isCarouselMessage, etc.)
- Inject biz node with interactive/native_flow for non-carousel interactive messages
- Inject biz + quality_control with decision_id for carousel messages
- Skip bot node for native_flow/carousel/catalog (breaks Web rendering)
- Force device-identity inclusion for carousel messages
- Append deferred biz/bot/quality_control nodes after device-identity and tctoken
- Store tctoken under both LID and PN for reliable lookup
Two fixes:
1. Stanza type="media" for carousel messages — WhatsApp Web needs this
to render carousel in real-time. Without it, only shows header until
page refresh (F5).
2. Fix jimp detection: typeof Jimp === 'function' (not just 'object')
so jpegThumbnail is generated for card images.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
7 corrections based on reverse-engineering of WhatsApp Business Android:
1. resolveCanonicalJid: PN→LID resolution for transaction lock keys (prevent race conditions)
2. Retain PN session during LID migration (avoid No Session errors)
3. Delayed PreKey deletion with 5-min grace period (prevent Invalid PreKey ID races)
4. Surgical session cleanup: only delete the specific corrupted device, not all devices
5. Identity dual storage: save identity key in both LID and PN addresses
6. MAC error cooldown reduced 10s→1s (faster recovery, aligned with WABA)
7. Allow session recreation on first retry (retryCount >= 1 instead of > 1)
* fix: remove unused jidDecode import from decode-wa-message.ts
* fix: add try/catch to delayed PreKey deletion to prevent unhandled rejection
The setTimeout async callback in removePreKey could cause unhandled promise
rejection if the keystore was destroyed (connection closed) before the
5-min grace period expired.
The web protocol (WA\x06\x03) requires a web-compatible UserAgent.
Using SMB_ANDROID in the UserAgent caused pair code registration to
fail with "cannot connect device" even though the phone confirmation
appeared. The Android identity is now only set in DeviceProps.platformType
(ANDROID_PHONE) in the registration node, which correctly determines
the display name in "Linked Devices" as "Android (14)".
Changes:
- getUserAgent(): always returns MACOS platform and Desktop device
- getClientPayload(): always includes webInfo (no longer skipped for Android)
- Remove unused isAndroidBrowser import from validate-connection.ts
- Update pair code comments documenting tested platform IDs
- Add structured logging to pair code request