From b5102ba4f98eb13af49d18a8142253d49141905a Mon Sep 17 00:00:00 2001 From: Eric Lytle Date: Mon, 2 Mar 2026 12:02:48 +0000 Subject: [PATCH] fix(hooks): add isGroup and groupId to message:sent context Adds group context fields to MessageSentHookContext so hooks can correlate sent events with received events for the same conversation. Previously, message:received included isGroup/groupId but message:sent did not, forcing hooks to use mismatched identifiers (e.g. groupId vs numeric chat ID) when tracking conversations. Fields are derived from MsgContext in dispatch-from-config and threaded through route-reply and deliver via the mirror parameter. Addresses feedback from matskevich (production user, 550+ events) reported on PR #6797. --- src/auto-reply/reply/dispatch-from-config.ts | 11 +++++++++++ src/auto-reply/reply/route-reply.ts | 6 ++++++ src/hooks/internal-hooks.ts | 4 ++++ src/infra/outbound/deliver.ts | 8 ++++++++ 4 files changed, 29 insertions(+) diff --git a/src/auto-reply/reply/dispatch-from-config.ts b/src/auto-reply/reply/dispatch-from-config.ts index 47b4209af85..835519e0cdc 100644 --- a/src/auto-reply/reply/dispatch-from-config.ts +++ b/src/auto-reply/reply/dispatch-from-config.ts @@ -106,6 +106,9 @@ export async function dispatchReplyFromConfig(params: { const sessionKey = ctx.SessionKey; const startTime = diagnosticsEnabled ? Date.now() : 0; const canTrackSession = diagnosticsEnabled && Boolean(sessionKey); + const isGroup = Boolean(ctx.GroupSubject || ctx.GroupChannel); + const groupId = + ctx.From?.includes(":group:") || ctx.From?.includes(":channel:") ? ctx.From : undefined; const recordProcessed = ( outcome: "completed" | "skipped" | "error", @@ -291,6 +294,8 @@ export async function dispatchReplyFromConfig(params: { cfg, abortSignal, mirror, + isGroup, + groupId, }); if (!result.ok) { logVerbose(`dispatch-from-config: route-reply failed: ${result.error ?? "unknown error"}`); @@ -316,6 +321,8 @@ export async function dispatchReplyFromConfig(params: { accountId: ctx.AccountId, threadId: ctx.MessageThreadId, cfg, + isGroup, + groupId, }); queuedFinal = result.ok; if (result.ok) { @@ -499,6 +506,8 @@ export async function dispatchReplyFromConfig(params: { accountId: ctx.AccountId, threadId: ctx.MessageThreadId, cfg, + isGroup, + groupId, }); if (!result.ok) { logVerbose( @@ -549,6 +558,8 @@ export async function dispatchReplyFromConfig(params: { accountId: ctx.AccountId, threadId: ctx.MessageThreadId, cfg, + isGroup, + groupId, }); queuedFinal = result.ok || queuedFinal; if (result.ok) { diff --git a/src/auto-reply/reply/route-reply.ts b/src/auto-reply/reply/route-reply.ts index e349c31e542..1c620d6e3ef 100644 --- a/src/auto-reply/reply/route-reply.ts +++ b/src/auto-reply/reply/route-reply.ts @@ -37,6 +37,10 @@ export type RouteReplyParams = { abortSignal?: AbortSignal; /** Mirror reply into session transcript (default: true when sessionKey is set). */ mirror?: boolean; + /** Whether this message is being sent in a group/channel context */ + isGroup?: boolean; + /** Group or channel identifier for correlation with received events */ + groupId?: string; }; export type RouteReplyResult = { @@ -145,6 +149,8 @@ export async function routeReply(params: RouteReplyParams): Promise {}); };