fix: store group messages from non-allowlisted senders as context
When groupPolicy is 'allowlist', messages from senders not in groupAllowFrom were completely dropped (allowed: false + continue). This meant the agent couldn't see messages from other bots or non-allowlisted group members in its context window. The docs state that non-mentioned messages should be 'stored for context only', but this only applied after access control passed. Changes: - Add storeForContext flag to InboundAccessControlResult - When a group message fails groupAllowFrom, set storeForContext: true instead of silently dropping - Pass contextOnly flag through to WebInboundMessage - In on-message handler, store contextOnly messages in group history without triggering a reply - Skip read receipts for context-only messages Fixes: group members not in groupAllowFrom are now visible in the agent's '[Chat messages since your last reply]' context block.
This commit is contained in:
parent
4de0bae45a
commit
85cc968aab
@ -94,6 +94,32 @@ export function createWebOnMessageHandler(params: {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Context-only messages: store for group history but never trigger a reply.
|
||||||
|
// This happens when a group message passes group allowlist checks for the group
|
||||||
|
// itself but the sender is not in groupAllowFrom.
|
||||||
|
if (msg.contextOnly && msg.chatType === "group") {
|
||||||
|
const sender =
|
||||||
|
msg.senderName && msg.senderE164
|
||||||
|
? `${msg.senderName} (${msg.senderE164})`
|
||||||
|
: (msg.senderName ?? msg.senderE164 ?? "Unknown");
|
||||||
|
logVerbose(`Storing context-only group message from ${sender} in ${conversationId}`);
|
||||||
|
const { recordPendingHistoryEntryIfEnabled } =
|
||||||
|
await import("../../../auto-reply/reply/history.js");
|
||||||
|
recordPendingHistoryEntryIfEnabled({
|
||||||
|
historyMap: params.groupHistories,
|
||||||
|
historyKey: groupHistoryKey,
|
||||||
|
limit: params.groupHistoryLimit,
|
||||||
|
entry: {
|
||||||
|
sender,
|
||||||
|
body: msg.body,
|
||||||
|
timestamp: msg.timestamp,
|
||||||
|
id: msg.id,
|
||||||
|
senderJid: msg.senderJid,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (msg.chatType === "group") {
|
if (msg.chatType === "group") {
|
||||||
const metaCtx = {
|
const metaCtx = {
|
||||||
From: msg.from,
|
From: msg.from,
|
||||||
|
|||||||
@ -10,6 +10,8 @@ import { resolveWhatsAppAccount } from "../accounts.js";
|
|||||||
|
|
||||||
export type InboundAccessControlResult = {
|
export type InboundAccessControlResult = {
|
||||||
allowed: boolean;
|
allowed: boolean;
|
||||||
|
/** When true the message should be stored as pending group context even though it did not pass sender allowlists. */
|
||||||
|
storeForContext: boolean;
|
||||||
shouldMarkRead: boolean;
|
shouldMarkRead: boolean;
|
||||||
isSelfChat: boolean;
|
isSelfChat: boolean;
|
||||||
resolvedAccountId: string;
|
resolvedAccountId: string;
|
||||||
@ -84,6 +86,7 @@ export async function checkInboundAccessControl(params: {
|
|||||||
logVerbose("Blocked group message (groupPolicy: disabled)");
|
logVerbose("Blocked group message (groupPolicy: disabled)");
|
||||||
return {
|
return {
|
||||||
allowed: false,
|
allowed: false,
|
||||||
|
storeForContext: false,
|
||||||
shouldMarkRead: false,
|
shouldMarkRead: false,
|
||||||
isSelfChat,
|
isSelfChat,
|
||||||
resolvedAccountId: account.accountId,
|
resolvedAccountId: account.accountId,
|
||||||
@ -94,6 +97,7 @@ export async function checkInboundAccessControl(params: {
|
|||||||
logVerbose("Blocked group message (groupPolicy: allowlist, no groupAllowFrom)");
|
logVerbose("Blocked group message (groupPolicy: allowlist, no groupAllowFrom)");
|
||||||
return {
|
return {
|
||||||
allowed: false,
|
allowed: false,
|
||||||
|
storeForContext: false,
|
||||||
shouldMarkRead: false,
|
shouldMarkRead: false,
|
||||||
isSelfChat,
|
isSelfChat,
|
||||||
resolvedAccountId: account.accountId,
|
resolvedAccountId: account.accountId,
|
||||||
@ -104,10 +108,11 @@ export async function checkInboundAccessControl(params: {
|
|||||||
(params.senderE164 != null && normalizedGroupAllowFrom.includes(params.senderE164));
|
(params.senderE164 != null && normalizedGroupAllowFrom.includes(params.senderE164));
|
||||||
if (!senderAllowed) {
|
if (!senderAllowed) {
|
||||||
logVerbose(
|
logVerbose(
|
||||||
`Blocked group message from ${params.senderE164 ?? "unknown sender"} (groupPolicy: allowlist)`,
|
`Group message from ${params.senderE164 ?? "unknown sender"} not in groupAllowFrom (storing for context)`,
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
allowed: false,
|
allowed: false,
|
||||||
|
storeForContext: true,
|
||||||
shouldMarkRead: false,
|
shouldMarkRead: false,
|
||||||
isSelfChat,
|
isSelfChat,
|
||||||
resolvedAccountId: account.accountId,
|
resolvedAccountId: account.accountId,
|
||||||
@ -121,6 +126,7 @@ export async function checkInboundAccessControl(params: {
|
|||||||
logVerbose("Skipping outbound DM (fromMe); no pairing reply needed.");
|
logVerbose("Skipping outbound DM (fromMe); no pairing reply needed.");
|
||||||
return {
|
return {
|
||||||
allowed: false,
|
allowed: false,
|
||||||
|
storeForContext: false,
|
||||||
shouldMarkRead: false,
|
shouldMarkRead: false,
|
||||||
isSelfChat,
|
isSelfChat,
|
||||||
resolvedAccountId: account.accountId,
|
resolvedAccountId: account.accountId,
|
||||||
@ -130,6 +136,7 @@ export async function checkInboundAccessControl(params: {
|
|||||||
logVerbose("Blocked dm (dmPolicy: disabled)");
|
logVerbose("Blocked dm (dmPolicy: disabled)");
|
||||||
return {
|
return {
|
||||||
allowed: false,
|
allowed: false,
|
||||||
|
storeForContext: false,
|
||||||
shouldMarkRead: false,
|
shouldMarkRead: false,
|
||||||
isSelfChat,
|
isSelfChat,
|
||||||
resolvedAccountId: account.accountId,
|
resolvedAccountId: account.accountId,
|
||||||
@ -172,6 +179,7 @@ export async function checkInboundAccessControl(params: {
|
|||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
allowed: false,
|
allowed: false,
|
||||||
|
storeForContext: false,
|
||||||
shouldMarkRead: false,
|
shouldMarkRead: false,
|
||||||
isSelfChat,
|
isSelfChat,
|
||||||
resolvedAccountId: account.accountId,
|
resolvedAccountId: account.accountId,
|
||||||
@ -182,6 +190,7 @@ export async function checkInboundAccessControl(params: {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
allowed: true,
|
allowed: true,
|
||||||
|
storeForContext: false,
|
||||||
shouldMarkRead: true,
|
shouldMarkRead: true,
|
||||||
isSelfChat,
|
isSelfChat,
|
||||||
resolvedAccountId: account.accountId,
|
resolvedAccountId: account.accountId,
|
||||||
|
|||||||
@ -190,9 +190,10 @@ export async function monitorWebInbox(options: {
|
|||||||
sock: { sendMessage: (jid, content) => sock.sendMessage(jid, content) },
|
sock: { sendMessage: (jid, content) => sock.sendMessage(jid, content) },
|
||||||
remoteJid,
|
remoteJid,
|
||||||
});
|
});
|
||||||
if (!access.allowed) continue;
|
if (!access.allowed && !access.storeForContext) continue;
|
||||||
|
const contextOnly = !access.allowed && access.storeForContext;
|
||||||
|
|
||||||
if (id && !access.isSelfChat && options.sendReadReceipts !== false) {
|
if (id && !contextOnly && !access.isSelfChat && options.sendReadReceipts !== false) {
|
||||||
const participant = msg.key?.participant;
|
const participant = msg.key?.participant;
|
||||||
try {
|
try {
|
||||||
await sock.readMessages([{ remoteJid, id, participant, fromMe: false }]);
|
await sock.readMessages([{ remoteJid, id, participant, fromMe: false }]);
|
||||||
@ -298,6 +299,7 @@ export async function monitorWebInbox(options: {
|
|||||||
sendMedia,
|
sendMedia,
|
||||||
mediaPath,
|
mediaPath,
|
||||||
mediaType,
|
mediaType,
|
||||||
|
contextOnly,
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
const task = Promise.resolve(debouncer.enqueue(inboundMessage));
|
const task = Promise.resolve(debouncer.enqueue(inboundMessage));
|
||||||
|
|||||||
@ -39,4 +39,6 @@ export type WebInboundMessage = {
|
|||||||
mediaType?: string;
|
mediaType?: string;
|
||||||
mediaUrl?: string;
|
mediaUrl?: string;
|
||||||
wasMentioned?: boolean;
|
wasMentioned?: boolean;
|
||||||
|
/** When true, message should only be stored for group context — not trigger a reply. */
|
||||||
|
contextOnly?: boolean;
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user