fix: add groupContextFromAll config for backward compat

- groupContextFromAll: boolean (default: false) - opt-in flag
- When enabled, group messages from senders not in groupAllowFrom
  are stored as pending context instead of being dropped
- Available at both top-level whatsapp config and per-account level
- Added to types, zod schema, resolved account, and access control
- Default false preserves existing behavior
This commit is contained in:
Adam Holt 2026-01-30 05:18:53 +00:00
parent 85cc968aab
commit ed87b1dd91
4 changed files with 28 additions and 2 deletions

View File

@ -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. */

View File

@ -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(),

View File

@ -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,

View File

@ -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,