diff --git a/src/config/types.whatsapp.ts b/src/config/types.whatsapp.ts index 85718ec19..74155756c 100644 --- a/src/config/types.whatsapp.ts +++ b/src/config/types.whatsapp.ts @@ -47,6 +47,12 @@ export type WhatsAppConfig = { * - "allowlist": only allow group messages from senders in groupAllowFrom/allowFrom */ groupPolicy?: GroupPolicy; + /** + * When true, messages from group members not in groupAllowFrom are still + * stored as pending context (visible in "[Chat messages since your last reply]") + * even though they cannot trigger a reply. Default: false (old behavior: drop entirely). + */ + groupContextFromAll?: boolean; /** Max group messages to keep as history context (0 disables). */ historyLimit?: number; /** Max DM turns to keep as history context. */ @@ -118,6 +124,8 @@ export type WhatsAppAccountConfig = { allowFrom?: string[]; groupAllowFrom?: string[]; groupPolicy?: GroupPolicy; + /** Store context from non-allowlisted group senders (default: false). */ + groupContextFromAll?: boolean; /** Max group messages to keep as history context (0 disables). */ historyLimit?: number; /** Max DM turns to keep as history context. */ diff --git a/src/config/zod-schema.providers-whatsapp.ts b/src/config/zod-schema.providers-whatsapp.ts index f9f6c6d26..c2968d54c 100644 --- a/src/config/zod-schema.providers-whatsapp.ts +++ b/src/config/zod-schema.providers-whatsapp.ts @@ -28,6 +28,7 @@ export const WhatsAppAccountSchema = z allowFrom: z.array(z.string()).optional(), groupAllowFrom: z.array(z.string()).optional(), groupPolicy: GroupPolicySchema.optional().default("allowlist"), + groupContextFromAll: z.boolean().optional().default(false), historyLimit: z.number().int().min(0).optional(), dmHistoryLimit: z.number().int().min(0).optional(), dms: z.record(z.string(), DmConfigSchema.optional()).optional(), @@ -85,6 +86,7 @@ export const WhatsAppConfigSchema = z allowFrom: z.array(z.string()).optional(), groupAllowFrom: z.array(z.string()).optional(), groupPolicy: GroupPolicySchema.optional().default("allowlist"), + groupContextFromAll: z.boolean().optional().default(false), historyLimit: z.number().int().min(0).optional(), dmHistoryLimit: z.number().int().min(0).optional(), dms: z.record(z.string(), DmConfigSchema.optional()).optional(), diff --git a/src/web/accounts.ts b/src/web/accounts.ts index c5010a741..698b0d5c6 100644 --- a/src/web/accounts.ts +++ b/src/web/accounts.ts @@ -20,6 +20,7 @@ export type ResolvedWhatsAppAccount = { allowFrom?: string[]; groupAllowFrom?: string[]; groupPolicy?: GroupPolicy; + groupContextFromAll?: boolean; dmPolicy?: DmPolicy; textChunkLimit?: number; chunkMode?: "length" | "newline"; @@ -150,6 +151,7 @@ export function resolveWhatsAppAccount(params: { allowFrom: accountCfg?.allowFrom ?? rootCfg?.allowFrom, groupAllowFrom: accountCfg?.groupAllowFrom ?? rootCfg?.groupAllowFrom, groupPolicy: accountCfg?.groupPolicy ?? rootCfg?.groupPolicy, + groupContextFromAll: accountCfg?.groupContextFromAll ?? rootCfg?.groupContextFromAll, textChunkLimit: accountCfg?.textChunkLimit ?? rootCfg?.textChunkLimit, chunkMode: accountCfg?.chunkMode ?? rootCfg?.chunkMode, mediaMaxMb: accountCfg?.mediaMaxMb ?? rootCfg?.mediaMaxMb, diff --git a/src/web/inbound/access-control.ts b/src/web/inbound/access-control.ts index ebb2ec218..d964ab884 100644 --- a/src/web/inbound/access-control.ts +++ b/src/web/inbound/access-control.ts @@ -53,6 +53,8 @@ export async function checkInboundAccessControl(params: { const groupAllowFrom = account.groupAllowFrom ?? (configuredAllowFrom && configuredAllowFrom.length > 0 ? configuredAllowFrom : undefined); + const groupContextFromAll = + account.groupContextFromAll ?? cfg.channels?.whatsapp?.groupContextFromAll ?? false; const isSamePhone = params.from === params.selfE164; const isSelfChat = isSelfChatMode(params.selfE164, configuredAllowFrom); const pairingGraceMs = @@ -107,12 +109,24 @@ export async function checkInboundAccessControl(params: { groupHasWildcard || (params.senderE164 != null && normalizedGroupAllowFrom.includes(params.senderE164)); if (!senderAllowed) { + if (groupContextFromAll) { + logVerbose( + `Group message from ${params.senderE164 ?? "unknown sender"} not in groupAllowFrom (storing for context)`, + ); + return { + allowed: false, + storeForContext: true, + shouldMarkRead: false, + isSelfChat, + resolvedAccountId: account.accountId, + }; + } logVerbose( - `Group message from ${params.senderE164 ?? "unknown sender"} not in groupAllowFrom (storing for context)`, + `Blocked group message from ${params.senderE164 ?? "unknown sender"} (groupPolicy: allowlist)`, ); return { allowed: false, - storeForContext: true, + storeForContext: false, shouldMarkRead: false, isSelfChat, resolvedAccountId: account.accountId,