feat(whatsapp): opt-in outbound-to-any-number (outboundOpen)#2
Merged
Conversation
WhatsApp is the only channel whose outbound resolver blocks sends to numbers not in `allowFrom`. Telegram, Discord, Google Chat and the core default treat `allowFrom` as inbound-only and send to any target. This adds an opt-in gate so the agent can message any number, while `dmPolicy`/`allowFrom` still gate who can trigger a reply. Gate: `plugins.entries.whatsapp.config.outboundOpen` (default false). `channels.whatsapp` is validated with `.strict()` (unknown keys rejected), but `plugins.entries.<id>.config` is a `z.record(z.unknown())` passthrough, so the flag lives there and is read from the resolved config in the outbound resolver. `resolveWhatsAppOutboundTarget` gains an `outboundPolicy` param: "open" skips the `allowFrom` check for plain numbers; group/newsletter JIDs are always allowed. Tests cover the resolver and the end-to-end resolveTarget path.
… release notes Regenerate clawnify-patches/whatsapp-clawnify-tools.patch against upstream v2026.6.5 to include the opt-in outbound-to-any-number change (plugins.entries.whatsapp.config.outboundOpen). Verified: applies cleanly onto a fresh v2026.6.5 checkout, builds, and 36 tests pass. Also make the GitHub Release name/body stop hardcoding a specific feature list (it had gone stale — still said "RAM only, never persisted" after persistence + on-demand history + profile tools shipped). The release now points to the patch as the authoritative source of the current feature set.
…ns.entries config The previous approach stored the flag under plugins.entries.whatsapp.config, which the plugin's manifest configSchema (additionalProperties:false, pluginHooks only) rejects — writing it made the gateway config invalid. Move it to a first-class `channels.whatsapp.outboundOpen` (boolean, per-account override), alongside dmPolicy/groupPolicy/allowFrom. - Declare `outboundOpen` in the WhatsApp config schema (buildWhatsAppCommonShape) + type, and regenerate bundled-channel-config-metadata so the plugin's generated channelConfigs schema validates it. - resolveTarget reads `channels.whatsapp.outboundOpen` (+ account override). - Tests updated to the channel field. Verified end to end on a live gateway: config validates, gateway restarts healthy, no schema rejection.
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.
What
Adds an opt-in gate so a WhatsApp agent can send messages to any number, while
dmPolicy/allowFromstill restrict who can trigger a reply (inbound stays gated). Default off.Why
allowFromis an inbound sender allowlist on every channel — Telegram, Discord, Google Chat, and the core default all let the agent send outbound to any target. WhatsApp is the only channel that also blocks outbound to non-allowlisted numbers (resolveWhatsAppOutboundTarget→ "not listed in the configured WhatsApp allowFrom policy"), which breaks the common "send to anyone, only accept from a list" model. This restores it, opt-in.How
resolveWhatsAppOutboundTargetgains anoutboundPolicyparam."open"skips theallowFromcheck for plain numbers; group/newsletter JIDs are always allowed (unchanged).channel-outbound.tsreads the effective policy fromplugins.entries.whatsapp.config.outboundOpen.Why
plugins.entriesconfig, notchannels.whatsapp.outboundPolicychannels.whatsappis validated with.strict()and rejects unknown keys.plugins.entries.<id>.configis az.record(z.unknown())passthrough, so the flag validates without a channel-schema change and is read from the resolved config in the outbound resolver.Tests
resolve-outbound-target.test.ts:openallows a non-allowlisted number;allowlist/unset still blocks.channel-outbound.test.ts: end-to-end viaresolveTargetwith acfg— blocks by default, opens onoutboundOpen: true, blocks onfalse.