From db055a5c0df182e3ae0458f27ac3bf962eb49711 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Wed, 22 Apr 2026 05:38:40 +0100 Subject: [PATCH] refactor: move WhatsApp group inference out of core --- src/config/sessions.test.ts | 42 +++++++++---------- src/config/sessions/group.ts | 21 +--------- ...hannel-contract-surface-guardrails.test.ts | 4 ++ 3 files changed, 27 insertions(+), 40 deletions(-) diff --git a/src/config/sessions.test.ts b/src/config/sessions.test.ts index 2c85e12a608..2e3098cc739 100644 --- a/src/config/sessions.test.ts +++ b/src/config/sessions.test.ts @@ -107,7 +107,7 @@ describe("sessions", () => { { name: "returns normalized per-sender key", scope: "per-sender" as const, - ctx: { From: "whatsapp:+1555" }, + ctx: { From: "chat:+1555" }, expected: "+1555", }, { @@ -125,14 +125,14 @@ describe("sessions", () => { { name: "keeps group chats distinct", scope: "per-sender" as const, - ctx: { From: "12345-678@g.us", ChatType: "group", Provider: "whatsapp" }, - expected: "whatsapp:group:12345-678@g.us", + ctx: { From: "room-123", ChatType: "group", Provider: "demo-chat" }, + expected: "demo-chat:group:room-123", }, { name: "prefixes group keys with provider when available", scope: "per-sender" as const, - ctx: { From: "12345-678@g.us", ChatType: "group", Provider: "whatsapp" }, - expected: "whatsapp:group:12345-678@g.us", + ctx: { From: "room-456", ChatType: "group", Provider: "demo-chat" }, + expected: "demo-chat:group:room-456", }, ] as const; @@ -179,7 +179,7 @@ describe("sessions", () => { { name: "maps direct chats to main key when provided", scope: "per-sender" as const, - ctx: { From: "whatsapp:+1555" }, + ctx: { From: "chat:+1555" }, mainKey: "main", expected: "agent:main:main", }, @@ -200,9 +200,9 @@ describe("sessions", () => { { name: "leaves groups untouched even with main key", scope: "per-sender" as const, - ctx: { From: "12345-678@g.us", ChatType: "group", Provider: "whatsapp" }, + ctx: { From: "room-123", ChatType: "group", Provider: "demo-chat" }, mainKey: "main", - expected: "agent:main:whatsapp:group:12345-678@g.us", + expected: "agent:main:demo-chat:group:room-123", }, ] as const; @@ -268,7 +268,7 @@ describe("sessions", () => { await updateLastRoute({ storePath, sessionKey: mainSessionKey, - channel: "whatsapp", + channel: "demo-chat", to: "111", accountId: "legacy", deliveryContext: { @@ -325,7 +325,7 @@ describe("sessions", () => { }); it("updateLastRoute records origin + group metadata when ctx is provided", async () => { - const sessionKey = "agent:main:whatsapp:group:123@g.us"; + const sessionKey = "agent:main:demo-chat:group:room-123"; const { storePath } = await createSessionStoreFixture({ prefix: "updateLastRoute", entries: {}, @@ -335,23 +335,23 @@ describe("sessions", () => { storePath, sessionKey, deliveryContext: { - channel: "whatsapp", - to: "123@g.us", + channel: "demo-chat", + to: "room-123", }, ctx: { - Provider: "whatsapp", + Provider: "demo-chat", ChatType: "group", GroupSubject: "Family", - From: "123@g.us", + From: "room-123", }, }); const store = loadSessionStore(storePath); expect(store[sessionKey]?.subject).toBe("Family"); - expect(store[sessionKey]?.channel).toBe("whatsapp"); - expect(store[sessionKey]?.groupId).toBe("123@g.us"); - expect(store[sessionKey]?.origin?.label).toBe("Family id:123@g.us"); - expect(store[sessionKey]?.origin?.provider).toBe("whatsapp"); + expect(store[sessionKey]?.channel).toBe("demo-chat"); + expect(store[sessionKey]?.groupId).toBe("room-123"); + expect(store[sessionKey]?.origin?.label).toBe("Family"); + expect(store[sessionKey]?.origin?.provider).toBe("demo-chat"); expect(store[sessionKey]?.origin?.chatType).toBe("group"); }); @@ -461,18 +461,18 @@ describe("sessions", () => { store["agent:main:main"] = { sessionId: "sess-normalized", updatedAt: Date.now(), - lastChannel: " WhatsApp ", + lastChannel: " Demo Chat ", lastTo: " +1555 ", lastAccountId: " acct-1 ", }; }); const store = loadSessionStore(storePath); - expect(store["agent:main:main"]?.lastChannel).toBe("whatsapp"); + expect(store["agent:main:main"]?.lastChannel).toBe("demo chat"); expect(store["agent:main:main"]?.lastTo).toBe("+1555"); expect(store["agent:main:main"]?.lastAccountId).toBe("acct-1"); expect(store["agent:main:main"]?.deliveryContext).toEqual({ - channel: "whatsapp", + channel: "demo chat", to: "+1555", accountId: "acct-1", }); diff --git a/src/config/sessions/group.ts b/src/config/sessions/group.ts index ad3952ac075..4e845f5ba65 100644 --- a/src/config/sessions/group.ts +++ b/src/config/sessions/group.ts @@ -15,19 +15,6 @@ type LegacyGroupSessionSurface = { resolveLegacyGroupSessionKey?: (ctx: MsgContext) => GroupKeyResolution | null; }; -function resolveImplicitGroupSurface(params: { - from: string; - normalizedChatType?: "group" | "channel"; -}): { provider: string; chatType: "group" | "channel" } | null { - if (params.from.endsWith("@g.us")) { - return { provider: "whatsapp", chatType: "group" }; - } - if (params.normalizedChatType) { - return null; - } - return null; -} - function resolveLegacyGroupSessionKey(ctx: MsgContext): GroupKeyResolution | null { for (const plugin of listChannelPlugins()) { const resolved = ( @@ -91,7 +78,6 @@ export function resolveGroupSessionKey(ctx: MsgContext): GroupKeyResolution | nu const chatType = normalizeOptionalLowercaseString(ctx.ChatType); const normalizedChatType = chatType === "channel" ? "channel" : chatType === "group" ? "group" : undefined; - const implicitGroupSurface = resolveImplicitGroupSurface({ from, normalizedChatType }); const legacyResolution = resolveLegacyGroupSessionKey(ctx); const looksLikeGroup = @@ -99,7 +85,6 @@ export function resolveGroupSessionKey(ctx: MsgContext): GroupKeyResolution | nu normalizedChatType === "channel" || from.includes(":group:") || from.includes(":channel:") || - implicitGroupSurface !== null || legacyResolution !== null; if (!looksLikeGroup) { return null; @@ -115,9 +100,7 @@ export function resolveGroupSessionKey(ctx: MsgContext): GroupKeyResolution | nu return legacyResolution; } - const provider = headIsSurface - ? head - : (providerHint ?? implicitGroupSurface?.provider ?? legacyResolution?.channel); + const provider = headIsSurface ? head : (providerHint ?? legacyResolution?.channel); if (!provider) { return null; } @@ -128,7 +111,7 @@ export function resolveGroupSessionKey(ctx: MsgContext): GroupKeyResolution | nu ? second : from.includes(":channel:") || normalizedChatType === "channel" ? "channel" - : (implicitGroupSurface?.chatType ?? "group"); + : "group"; const id = headIsSurface ? secondIsKind ? parts.slice(2).join(":") diff --git a/src/secrets/channel-contract-surface-guardrails.test.ts b/src/secrets/channel-contract-surface-guardrails.test.ts index ce2ee815623..230559ceef3 100644 --- a/src/secrets/channel-contract-surface-guardrails.test.ts +++ b/src/secrets/channel-contract-surface-guardrails.test.ts @@ -58,6 +58,10 @@ const CORE_SECRET_SURFACE_GUARDS = [ path: "src/channels/model-overrides.ts", forbiddenPatterns: [/\bfeishu\b/], }, + { + path: "src/config/sessions/group.ts", + forbiddenPatterns: [/\bwhatsapp\b/, /@g\.us/], + }, { path: "src/media-understanding/defaults.ts", forbiddenPatterns: [