feat(connections): WhatsApp-Web personal-account QR pairing (Baileys, opt-in)#10
Merged
Merged
Conversation
… opt-in) Pair a personal WhatsApp account by scanning the rotating QR in Vesper World or the terminal — extends scan-to-connect to the personal-account channel. Baileys is isolated in a new opt-in @vesper/channel-whatsapp-web package (the sole runtime dependency); core/ui never reference it, the cli declares it as an optionalDependency + lazy-loads it via a variable-specifier dynamic import, so it stays out of the static graph + compiled binary and is omittable. - core: extensible channel-plugin registry (registerChannelPlugin) + whatsapp-web catalog/ChannelId (transport qr-web) + pairingNeedsInbound flag — zero Baileys in core - package: WhatsAppWebHandler (ChannelHandler + Pairable) with an injected socket factory; vault-backed Baileys session (single whatsapp_web_session blob); rotating-QR pairing (async-queue bridge), two-way receive, send over the live socket - daemon: loadOptionalChannels() registers the package at boot; coordinator gains a self-driving pairing path (skips authenticate + inbound multiplex; enables on link without a chatId) — Telegram/Discord unchanged - contract: .ai/context.md carves out the one opt-in dependency (Baileys is a channel transport, not an LLM SDK — Hard rule 12 intact); sync:ai regenerated 870 tests / 0 fail; biome clean; Baileys 7.0.0-rc13. Authorized by Omar 2026-06-05. Record per Rule 11 (issue cap): cycle-log.md.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Extends scan-to-connect (#9) to the personal-account channel: pair a personal WhatsApp by scanning the rotating QR in Vesper World or the terminal. Two-way once paired.
Unlike Telegram/Discord (bot-token + chat-link QR), WhatsApp-Web pairing is self-driving — the scan itself establishes the session — so it needs a reverse-engineered WhatsApp-Web client. That dependency (Baileys) is contained in a single opt-in package.
Isolation (the design constraint)
registerChannelPlugin);whatsapp-webis a catalog id +ChannelId(transportqr-web) whoseavailable/pairablederive from runtime registration.@vesper/channel-whatsapp-web— the sole runtime dependency.core+uinever reference it; theclideclares it as anoptionalDependencyand the daemon lazy-loads it at boot via a variable-specifier dynamicimport()(loadOptionalChannels), so it stays out of tsc resolution + the compiled binary's static graph and a distributed build can--omit=optional.What's included
WhatsAppWebHandler(ChannelHandler+Pairable) with an injectedWASocketFactoryseam (tests inject a fake — no live WhatsApp, no real socket).makeVaultAuthStateports Baileys'useMultiFileAuthStateto a single vault blob (whatsapp_web_session,BufferJSON-serialized).startPairingbridgesconnection.updateto an async queue so the rotating QR yields repeatedawaiting;open→ save +linked; two-wayreceive;sendover the live socket.pairingNeedsInbound: false): skips the authenticate precondition + the inbound multiplex, and enables-on-link even without a chat id. Telegram/Discord behavior is byte-identical (defaulttrue)..ai/context.mdcarves out the one opt-in dependency (cites the 2026-06-05 authorization; does not relax Hard rule 12 — Baileys is a channel transport, not an LLM SDK; not a precedent).bun run sync:airegeneratedAGENTS.md+rules.mdc.Notes / known limitations
7.0.0-rc13(thelatestdist-tag): current protocol, but an RC that pulls a native Rust bridge (whatsapp-rust-bridge) + libsignal/protobuf. Chosen deliberately over the Rust-freelegacy6.x. Installed + runtime-load verified.vesper connections list(CLI process) showswhatsapp-webas unavailable — the plugin registers only in the daemon (the UI/daemon is the source of truth; the CLI doesn't load Baileys just to list). The compiledvesper-desktopbinary omits it until Launch wires it.Test plan
bun test— 870 / 0 fail (15 package tests: vault auth-state round-trip, rotating-QR pairing, linked/error/expired, receive mapping, send-guard; + 1 lazy-registration integration test).biome ciclean.tsc --noEmitadds no new errors; the new package'ssrc/is clean.whatsapp-webcatalog addition broke zero existing tests.