WhatsApp: add group and direct system prompt support (#59553)

Merged via squash.

Prepared head SHA: 63e2b50e01
Co-authored-by: Bluetegu <1525690+Bluetegu@users.noreply.github.com>
Co-authored-by: omarshahine <10343873+omarshahine@users.noreply.github.com>
Reviewed-by: @omarshahine
This commit is contained in:
Ron Cohen
2026-04-22 02:40:32 +03:00
committed by GitHub
parent 06a6dd5a6b
commit 08bc16853e
12 changed files with 319 additions and 1 deletions

View File

@@ -36,6 +36,7 @@ export type ResolvedWhatsAppAccount = {
ackReaction?: WhatsAppAccountConfig["ackReaction"];
reactionLevel?: WhatsAppAccountConfig["reactionLevel"];
groups?: WhatsAppAccountConfig["groups"];
direct?: WhatsAppAccountConfig["direct"];
debounceMs?: number;
};
@@ -150,6 +151,7 @@ export function resolveWhatsAppAccount(params: {
ackReaction: merged.ackReaction,
reactionLevel: merged.reactionLevel,
groups: merged.groups,
direct: merged.direct,
debounceMs: merged.debounceMs,
};
}

View File

@@ -194,6 +194,44 @@ describe("whatsapp inbound dispatch", () => {
expect(ctx.To).toBe("+2000");
});
it("passes groupSystemPrompt into GroupSystemPrompt for group chats", () => {
const ctx = buildWhatsAppInboundContext({
combinedBody: "hi",
conversationId: "123@g.us",
groupSystemPrompt: "Specific group prompt",
msg: makeMsg({ from: "123@g.us", chatType: "group", groupParticipants: [] }),
route: makeRoute({ sessionKey: "agent:main:whatsapp:group:123@g.us" }),
sender: { e164: "+15550002222" },
});
expect(ctx.GroupSystemPrompt).toBe("Specific group prompt");
});
it("passes groupSystemPrompt into GroupSystemPrompt for direct chats", () => {
const ctx = buildWhatsAppInboundContext({
combinedBody: "hi",
conversationId: "+1555",
groupSystemPrompt: "Specific direct prompt",
msg: makeMsg({ from: "+1555", chatType: "direct" }),
route: makeRoute({ sessionKey: "agent:main:whatsapp:direct:+1555" }),
sender: { e164: "+1555" },
});
expect(ctx.GroupSystemPrompt).toBe("Specific direct prompt");
});
it("omits GroupSystemPrompt when groupSystemPrompt is not provided", () => {
const ctx = buildWhatsAppInboundContext({
combinedBody: "hi",
conversationId: "123@g.us",
msg: makeMsg({ from: "123@g.us", chatType: "group", groupParticipants: [] }),
route: makeRoute({ sessionKey: "agent:main:whatsapp:group:123@g.us" }),
sender: { e164: "+15550002222" },
});
expect(ctx.GroupSystemPrompt).toBeUndefined();
});
it("defaults responsePrefix to identity name in self-chats when unset", () => {
const responsePrefix = resolveWhatsAppResponsePrefix({
cfg: {

View File

@@ -87,6 +87,7 @@ export function buildWhatsAppInboundContext(params: {
conversationId: string;
groupHistory?: GroupHistoryEntry[];
groupMemberRoster?: Map<string, string>;
groupSystemPrompt?: string;
msg: WebInboundMsg;
route: ReturnType<typeof resolveAgentRoute>;
sender: SenderContext;
@@ -132,6 +133,7 @@ export function buildWhatsAppInboundContext(params: {
SenderE164: params.sender.e164,
CommandAuthorized: params.commandAuthorized,
WasMentioned: params.msg.wasMentioned,
GroupSystemPrompt: params.groupSystemPrompt,
...(params.msg.location ? toLocationContext(params.msg.location) : {}),
Provider: "whatsapp",
Surface: "whatsapp",

View File

@@ -1,3 +1,7 @@
import {
resolveWhatsAppDirectSystemPrompt,
resolveWhatsAppGroupSystemPrompt,
} from "../../system-prompt.js";
import { getPrimaryIdentityId, getSelfIdentity, getSenderIdentity } from "../../identity.js";
import {
resolveWhatsAppCommandAuthorized,
@@ -227,12 +231,25 @@ export async function processMessage(params: {
pipelineResponsePrefix: replyPipeline.responsePrefix,
});
// Resolve combined conversation system prompt using the group or direct surface.
const conversationSystemPrompt =
params.msg.chatType === "group"
? resolveWhatsAppGroupSystemPrompt({
accountConfig: account,
groupId: conversationId,
})
: resolveWhatsAppDirectSystemPrompt({
accountConfig: account,
peerId: dmRouteTarget ?? params.msg.from,
});
const ctxPayload = buildWhatsAppInboundContext({
combinedBody,
commandAuthorized,
conversationId,
groupHistory: visibleGroupHistory,
groupMemberRoster: params.groupMemberNames.get(params.groupHistoryKey),
groupSystemPrompt: conversationSystemPrompt,
msg: params.msg,
route: params.route,
sender: {

View File

@@ -0,0 +1,29 @@
export function resolveWhatsAppGroupSystemPrompt(params: {
accountConfig?: { groups?: Record<string, { systemPrompt?: string }> } | null;
groupId?: string | null;
}): string | undefined {
if (!params.groupId) {
return undefined;
}
const groups = params.accountConfig?.groups;
return (
groups?.[params.groupId]?.systemPrompt?.trim() ||
groups?.["*"]?.systemPrompt?.trim() ||
undefined
);
}
export function resolveWhatsAppDirectSystemPrompt(params: {
accountConfig?: { direct?: Record<string, { systemPrompt?: string }> } | null;
peerId?: string | null;
}): string | undefined {
if (!params.peerId) {
return undefined;
}
const direct = params.accountConfig?.direct;
return (
direct?.[params.peerId]?.systemPrompt?.trim() ||
direct?.["*"]?.systemPrompt?.trim() ||
undefined
);
}