PRs 124-129 replaced TypeScript non-null assertions (!) with optional
chaining (?.) and empty string defaults (?? ''). While defensive
programming is usually good, in Signal protocol and WhatsApp binary
node handling code these changes caused:
- Malformed JIDs with empty user components (e.g. @s.whatsapp.net)
- Silent device enumeration failures (messages skip recipients)
- Wrong own-device detection (message routing errors)
- Broken prekey count handling (prekey uploads silently skipped)
- Wrong retry count tracking in Signal protocol
These empty-string defaults are dangerous because Signal session
lookups fail for malformed JIDs, causing "SessionError: No sessions".
Reverted to original Baileys pattern using non-null assertions (!)
which match the upstream reference (infinitezap/Teste_InfiniteAPI).
Files fixed: messages-send.ts, messages-recv.ts, socket.ts, chats.ts,
groups.ts, communities.ts, business.ts
https://claude.ai/code/session_01XaA7GwNaB6azTHFYQ8WEpB
The previous PRs (124-129) replaced non-null assertions (!) with defensive
null checks + continue statements in parseAndInjectE2ESessions and
extractDeviceJids. This caused ALL prekey bundles to be silently skipped
because the continue statements would trigger whenever any sub-field
appeared missing (even though WhatsApp always sends complete bundles).
Result: no Signal sessions were created after QR scan, causing
"SessionError: No sessions" when trying to send messages.
Reverted to the original Baileys pattern using non-null assertions,
which matches the upstream reference (infinitezap/Teste_InfiniteAPI).
https://claude.ai/code/session_01XaA7GwNaB6azTHFYQ8WEpB
The whatsapp-rust-bridge package contains a top-level await on an inline
WASM binary, which propagates through the ESM graph and causes
ERR_REQUIRE_ASYNC_MODULE when CJS consumers try to require() this package.
Replace all static imports from whatsapp-rust-bridge with a lazy-loading
bridge module (wasm-bridge.ts) that uses import().then() instead of
top-level await, keeping the dependency out of the static ESM graph.
https://claude.ai/code/session_01XaA7GwNaB6azTHFYQ8WEpB
Fixes:
- chats.ts: revert encodeResult/initial to ! (guaranteed by callback assignment)
- groups.ts: fix operator precedence with ?? (wrap +attrs in parens), fix groupId type
- messages-recv.ts: extract messageKey.id to local const with fallback
- socket.ts: revert onClose! (guaranteed by synchronous callback assignment)
- event-buffer.ts: add 'notify' fallback for MessageUpsertType
- messages.ts: add filePath const after null guard, fix contextInfo type assertion
- noise-handler.ts: restore array index ! (guaranteed by length check)
Build now compiles with zero new errors.
https://claude.ai/code/session_01E2cfX1N3sJgCJBTvzGazSG
Eliminates command injection vulnerability (CWE-78) by switching from
shell-based exec() to execFile() which passes arguments as an array
without spawning a shell. Behavior is identical — same ffmpeg args,
same output — but injection via crafted paths is now impossible.
https://claude.ai/code/session_01E2cfX1N3sJgCJBTvzGazSG
Merge upstream WhiskeySockets/Baileys master using 'ours' strategy.
This marks the 5 upstream commits as merged without changing any
files in our repository, removing the 'commits behind' message on GitHub.
https://claude.ai/code/session_01E2cfX1N3sJgCJBTvzGazSG
Surgically applies the changes from WhiskeySockets/Baileys commit b5c1741
("feat: replace async crypto with sync Rust WASM" by jlucaso1) while
preserving all custom modifications (interactive messages, carousels,
albums, sticker packs, native flow buttons, Prometheus metrics, etc).
Changes:
- Replace async hkdf/md5 (Web Crypto API) with sync re-exports from whatsapp-rust-bridge@0.5.2
- Replace LTHash class with LTHashAntiTampering from WASM
- Replace mutationKeys() with expandAppStateKeys() from WASM
- Remove ~25 unnecessary await keywords across crypto call chain
- Update Buffer→Uint8Array types for MediaDecryptionKeyInfo and internal crypto functions
- Make noise handshake, media retry encrypt/decrypt, and reporting token generation synchronous
Performance impact:
- Eliminates Promise overhead on every HKDF/LTHash operation
- Significant improvement during app state sync (hundreds of mutations per reconnection)
- Sync crypto reduces event loop pressure under high session load
Custom code preserved (zero conflicts):
- messages-send.ts: All interactive message, carousel, album, sticker pack logic intact
- Types/Message.ts: All custom types (NativeFlowButton, Carousel, Album, etc.) intact
- All Prometheus metrics, circuit breakers, session TTL logic intact
https://claude.ai/code/session_01Ffc5YrPuqv8N9SwEuSM8mr
Pastorini's carousel renders on WhatsApp Web. Key differences found
by comparing logs and screenshot:
1. Direct interactiveMessage at root (NO viewOnceMessage wrapper)
- messageKeys: ['interactiveMessage'] in Pastorini logs
- Previous attempts with viewOnce V1/V2 all failed on Web
2. Root header WITH title + hasMediaAttachment: false (restored)
3. messageVersion: 1 in carouselMessage (restored)
4. tctoken included in stanza (was being skipped for carousel)
- Pastorini stanza: ['participants','device-identity','tctoken','biz']
5. messageContextInfo at message root level (kept)
https://claude.ai/code/session_018DkDxsjWzM131jy3ivWjZp
Compare with ckptw's working carousel implementation:
1. Remove root header from interactiveMessage - ckptw does NOT set
a header on the root, only on individual cards. The root header
was incorrectly believed to prevent error 479.
2. Remove messageVersion from carouselMessage - ckptw doesn't set it.
Structure now matches ckptw exactly:
viewOnceMessage > message > {
messageContextInfo,
interactiveMessage: {
body, footer,
carouselMessage: { cards } // no messageVersion
// no header at root level
}
}
https://claude.ai/code/session_018DkDxsjWzM131jy3ivWjZp
1. Switch wrapper from viewOnceMessageV2 (field 55) to viewOnceMessage V1
(field 37). V2 renders on mobile but NOT on WhatsApp Web/Desktop.
V1 is what ckptw, Vkazee, and most working Baileys forks use.
Previous error 479 with V1 was caused by missing root header and
fromObject() corruption - both now fixed.
2. Stop skipping own linked devices for carousel messages. This was
preventing the sender's WhatsApp Web from receiving the carousel.
3. Allow DSM (deviceSentMessage) wrapper for carousel - no longer
skip it for own devices or retry paths.
https://claude.ai/code/session_018DkDxsjWzM131jy3ivWjZp
1. Suppress verbose "Closing session: SessionEntry {...}" logs from
libsignal that dump cryptographic keys/ratchets to console.log
2. Add warning when carousel cards don't have media attachments -
WhatsApp Web requires every card to have hasMediaAttachment:true
with actual image/video (per ckptw reference implementation)
3. Add carousel structure summary log for debugging card composition
https://claude.ai/code/session_018DkDxsjWzM131jy3ivWjZp
Remove per-device and relay-level carousel debug logging that dumped
full base64-encoded protobuf bytes and JSON structures to the logs.
These were temporary debugging aids that generated excessive output.
https://claude.ai/code/session_018DkDxsjWzM131jy3ivWjZp
The messageContextInfo with deviceListMetadata was only inside the V2 wrapper.
relayMessage copies message.messageContextInfo to meMsg for sender's own
devices (DSM), so it needs to be at the outer level too.
https://claude.ai/code/session_018DkDxsjWzM131jy3ivWjZp
Switch carousel from direct interactiveMessage to viewOnceMessageV2
wrapper (field 55), confirmed by Z-API as the stable approach for
WhatsApp Web rendering from non-Cloud API accounts.
Key changes:
- Wrap carousel in viewOnceMessageV2 > message > interactiveMessage
(V1 caused error 479, direct interactiveMessage didn't render on Web)
- Add messageContextInfo with deviceListMetadata and version 2 for
multi-device rendering compatibility
- Update all helper functions (getButtonType, isCarouselMessage,
isCatalogMessage, isListNativeFlow, getButtonArgs) to check V2 path
- Update per-device and biz node debug logging for V2 detection
https://claude.ai/code/session_018DkDxsjWzM131jy3ivWjZp
Pastorini's working implementation confirms carousels must be sent as
direct interactiveMessage at the message root level, NOT wrapped in
viewOnceMessage. The viewOnce wrapper prevented rendering on WhatsApp
Web and delivery to Apple/iOS users.
https://claude.ai/code/session_018DkDxsjWzM131jy3ivWjZp
Three changes matching Pastorini's working implementation:
1. Always set root header in generateCarouselMessage - the root
interactiveMessage header must always be present with title and
hasMediaAttachment:false. Previously it was undefined when no text
was provided, which violates WhatsApp MD protocol requirements.
2. Return plain JS object from generateWAMessageContent for carousel -
skip WAProto.Message.fromObject() which can corrupt nested carousel
structures by incorrectly handling oneOf fields in deeply nested
InteractiveMessage cards. protobuf encode() handles plain objects
correctly during serialization.
3. Pass plain JS object directly to relayMessage in sendMessage - call
relayMessage(jid, msgContent) with the plain object instead of
going through proto.WebMessageInfo.fromObject() first. This matches
Pastorini's approach of relayMessage(jid, plainObject, opts).
https://claude.ai/code/session_018DkDxsjWzM131jy3ivWjZp
Debug logging revealed that error 479 comes exclusively from the
sender's own linked devices (WhatsApp Web/Desktop) when they receive
the carousel wrapped in deviceSentMessage (DSM). Recipient devices
receive the raw carousel (without DSM) and process it correctly.
The fix skips sending carousel to sender's own linked devices:
- Skip meRecipients (own devices) when message is carousel
- Skip DSM wrapper in createParticipantNodes for carousel
- Skip DSM in retry path for own devices when carousel
The carousel still renders correctly on:
- Sender's phone (initiator)
- All recipient devices (phone + Web/Desktop)
https://claude.ai/code/session_01EK9NpViRCtda1WAvFd8ptR
- Remove hasSubtitle (field 10) from Header proto definition, index.js
and index.d.ts - this field was adding extra bytes to encoded protobuf
that working implementations don't send, potentially causing rejection
- Remove hasSubtitle from carousel card headers and root header
- Add [CAROUSEL DEBUG] logging in relayMessage to dump:
- Encoded message bytes as base64 (for binary comparison)
- Message structure as JSON
- Per-device encoded bytes with DSM flag
- This enables byte-level comparison with working implementations
https://claude.ai/code/session_01EK9NpViRCtda1WAvFd8ptR
Implementação completa baseada na análise ponto-a-ponto de 5 diferenças:
1. hasSubtitle adicionado ao proto schema (field 10, bool em Header)
- Adicionado ao WAProto.proto, index.js (encode/decode/fromObject/toObject)
- Adicionado ao index.d.ts (IHeader, Header class)
- Usado no root header e em cada card do carousel
2. relayMessage direto para carousel em sendMessage
- Detecta nativeCarousel no content e bypassa generateWAMessage inteiro
- Chama generateWAMessageContent (que usa fromObject) diretamente
- Depois relayMessage sem passar por generateWAMessageFromContent
- Elimina: segundo WAProto.Message.create(), contextInfo.expiration, etc.
3. Pipeline final do carousel agora:
sendMessage → generateWAMessageContent(fromObject) → relayMessage
Sem: generateWAMessageFromContent, WAProto.Message.create, ephemeral
Commits anteriores já incluem:
- viewOnceMessage wrapper
- fromObject no generateWAMessageContent
- Skip tctoken no stanza
https://claude.ai/code/session_01EK9NpViRCtda1WAvFd8ptR
Três correções para eliminar erro 479 em dispositivos vinculados:
1. Skip WAProto.Message.create() em generateWAMessageFromContent para carousel
- O carousel já foi processado com fromObject() (conversão profunda)
- O segundo create() faz cópia rasa que pode perder tipos protobuf nested
- Preserva a estrutura deep: cards > headers > imageMessage > nativeFlowMessage
2. Skip tctoken no stanza para carousel
- Implementações que funcionam não incluem tctoken para carousel
- Pode causar rejeição 479 em dispositivos vinculados (Web/Desktop)
3. Skip contextInfo.expiration (ephemeral) para carousel
- Se mensagens temporárias estão ativadas, contextInfo.expiration era
adicionado ao interactiveMessage, o que pode causar 479
- Implementações que funcionam não passam por generateWAMessageFromContent
https://claude.ai/code/session_01EK9NpViRCtda1WAvFd8ptR
Mudanças críticas para renderização do carousel em dispositivos vinculados:
1. Wrap carousel em viewOnceMessage para compatibilidade multi-device (MD)
2. Usar WAProto.Message.fromObject() ao invés de create() para conversão
profunda das estruturas nested (header, cards, nativeFlowMessage)
3. Adicionar root header com título no interactiveMessage
4. Retorno antecipado do carousel (sem messageContextInfo/reportingToken)
A diferença entre fromObject e create:
- fromObject: converte recursivamente objetos JS para tipos protobuf
- create: cópia rasa que pode não codificar corretamente estruturas deep
https://claude.ai/code/session_01EK9NpViRCtda1WAvFd8ptR