fix: prevent multi-channel response routing race condition (#4530)
## Problem When multiple channels (WhatsApp, iMessage, Telegram, etc.) are active, responses intended for one channel could leak to another channel if messages arrived simultaneously. This created serious privacy concerns. **Root Cause:** All channels were updating the SAME main session key with their delivery context (channel, to, accountId). When messages arrived concurrently: 1. WhatsApp message arrives → updates session['agent:main:main'].lastChannel = 'whatsapp' 2. Agent starts processing (takes time) 3. iMessage message arrives → OVERWRITES session['agent:main:main'].lastChannel = 'imessage' 4. WhatsApp response delivers → reads session lastChannel = 'imessage' 5. Response goes to wrong channel! ## Solution Changed all channel monitors to use the channel-specific session key (route.sessionKey) instead of the shared main session key (route.mainSessionKey) when updating delivery context. Now each channel updates its own isolated session: - WhatsApp: session['agent:main:whatsapp:dm:+1234'] - iMessage: session['agent:main:imessage:dm:alice'] - Telegram: session['agent:main:telegram:dm:12345'] This prevents cross-channel state clobbering. ## Changes - src/discord/monitor/message-handler.process.ts - src/imessage/monitor/monitor-provider.ts - src/line/bot-message-context.ts - src/signal/monitor/event-handler.ts - src/slack/monitor/message-handler/dispatch.ts - src/slack/monitor/message-handler/prepare.ts - src/telegram/bot-message-context.ts - src/web/auto-reply/monitor/process-message.ts All changed from: sessionKey: route.mainSessionKey To: sessionKey: route.sessionKey ## Testing Manual testing needed: 1. Configure 2+ channels (e.g., WhatsApp + iMessage) 2. Send message to channel A 3. Immediately send message to channel B (within 100ms) 4. Verify responses go to correct channels Fixes #4530
This commit is contained in:
parent
d2c1fd3c61
commit
e31764fed1
@ -302,7 +302,7 @@ export async function processDiscordMessage(ctx: DiscordMessagePreflightContext)
|
|||||||
ctx: ctxPayload,
|
ctx: ctxPayload,
|
||||||
updateLastRoute: isDirectMessage
|
updateLastRoute: isDirectMessage
|
||||||
? {
|
? {
|
||||||
sessionKey: route.mainSessionKey,
|
sessionKey: route.sessionKey,
|
||||||
channel: "discord",
|
channel: "discord",
|
||||||
to: `user:${author.id}`,
|
to: `user:${author.id}`,
|
||||||
accountId: route.accountId,
|
accountId: route.accountId,
|
||||||
|
|||||||
@ -510,7 +510,7 @@ export async function monitorIMessageProvider(opts: MonitorIMessageOpts = {}): P
|
|||||||
updateLastRoute:
|
updateLastRoute:
|
||||||
!isGroup && updateTarget
|
!isGroup && updateTarget
|
||||||
? {
|
? {
|
||||||
sessionKey: route.mainSessionKey,
|
sessionKey: route.sessionKey,
|
||||||
channel: "imessage",
|
channel: "imessage",
|
||||||
to: updateTarget,
|
to: updateTarget,
|
||||||
accountId: route.accountId,
|
accountId: route.accountId,
|
||||||
|
|||||||
@ -282,7 +282,7 @@ export async function buildLineMessageContext(params: BuildLineMessageContextPar
|
|||||||
if (!isGroup) {
|
if (!isGroup) {
|
||||||
await updateLastRoute({
|
await updateLastRoute({
|
||||||
storePath,
|
storePath,
|
||||||
sessionKey: route.mainSessionKey,
|
sessionKey: route.sessionKey,
|
||||||
deliveryContext: {
|
deliveryContext: {
|
||||||
channel: "line",
|
channel: "line",
|
||||||
to: userId ?? peerId,
|
to: userId ?? peerId,
|
||||||
@ -432,7 +432,7 @@ export async function buildLinePostbackContext(params: {
|
|||||||
if (!isGroup) {
|
if (!isGroup) {
|
||||||
await updateLastRoute({
|
await updateLastRoute({
|
||||||
storePath,
|
storePath,
|
||||||
sessionKey: route.mainSessionKey,
|
sessionKey: route.sessionKey,
|
||||||
deliveryContext: {
|
deliveryContext: {
|
||||||
channel: "line",
|
channel: "line",
|
||||||
to: userId ?? peerId,
|
to: userId ?? peerId,
|
||||||
|
|||||||
@ -156,7 +156,7 @@ export function createSignalEventHandler(deps: SignalEventHandlerDeps) {
|
|||||||
ctx: ctxPayload,
|
ctx: ctxPayload,
|
||||||
updateLastRoute: !entry.isGroup
|
updateLastRoute: !entry.isGroup
|
||||||
? {
|
? {
|
||||||
sessionKey: route.mainSessionKey,
|
sessionKey: route.sessionKey,
|
||||||
channel: "signal",
|
channel: "signal",
|
||||||
to: entry.senderRecipient,
|
to: entry.senderRecipient,
|
||||||
accountId: route.accountId,
|
accountId: route.accountId,
|
||||||
|
|||||||
@ -27,7 +27,7 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag
|
|||||||
});
|
});
|
||||||
await updateLastRoute({
|
await updateLastRoute({
|
||||||
storePath,
|
storePath,
|
||||||
sessionKey: route.mainSessionKey,
|
sessionKey: route.sessionKey,
|
||||||
deliveryContext: {
|
deliveryContext: {
|
||||||
channel: "slack",
|
channel: "slack",
|
||||||
to: `user:${message.user}`,
|
to: `user:${message.user}`,
|
||||||
|
|||||||
@ -533,7 +533,7 @@ export async function prepareSlackMessage(params: {
|
|||||||
ctx: ctxPayload,
|
ctx: ctxPayload,
|
||||||
updateLastRoute: isDirectMessage
|
updateLastRoute: isDirectMessage
|
||||||
? {
|
? {
|
||||||
sessionKey: route.mainSessionKey,
|
sessionKey: route.sessionKey,
|
||||||
channel: "slack",
|
channel: "slack",
|
||||||
to: `user:${message.user}`,
|
to: `user:${message.user}`,
|
||||||
accountId: route.accountId,
|
accountId: route.accountId,
|
||||||
|
|||||||
@ -617,7 +617,7 @@ export const buildTelegramMessageContext = async ({
|
|||||||
ctx: ctxPayload,
|
ctx: ctxPayload,
|
||||||
updateLastRoute: !isGroup
|
updateLastRoute: !isGroup
|
||||||
? {
|
? {
|
||||||
sessionKey: route.mainSessionKey,
|
sessionKey: route.sessionKey,
|
||||||
channel: "telegram",
|
channel: "telegram",
|
||||||
to: String(chatId),
|
to: String(chatId),
|
||||||
accountId: route.accountId,
|
accountId: route.accountId,
|
||||||
|
|||||||
@ -295,7 +295,7 @@ export async function processMessage(params: {
|
|||||||
cfg: params.cfg,
|
cfg: params.cfg,
|
||||||
backgroundTasks: params.backgroundTasks,
|
backgroundTasks: params.backgroundTasks,
|
||||||
storeAgentId: params.route.agentId,
|
storeAgentId: params.route.agentId,
|
||||||
sessionKey: params.route.mainSessionKey,
|
sessionKey: params.route.sessionKey,
|
||||||
channel: "whatsapp",
|
channel: "whatsapp",
|
||||||
to: dmRouteTarget,
|
to: dmRouteTarget,
|
||||||
accountId: params.route.accountId,
|
accountId: params.route.accountId,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user