From ad1fedd6ba07fdeb0e4e3438845b268e1eb592b5 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Fri, 13 Mar 2026 14:10:43 -0700 Subject: [PATCH] fix(hooks): preserve observers after inbound claim --- src/auto-reply/reply/dispatch-from-config.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/auto-reply/reply/dispatch-from-config.ts b/src/auto-reply/reply/dispatch-from-config.ts index 0f8ca00f8e1..a4b797c2eb5 100644 --- a/src/auto-reply/reply/dispatch-from-config.ts +++ b/src/auto-reply/reply/dispatch-from-config.ts @@ -186,6 +186,7 @@ export async function dispatchReplyFromConfig(params: { const hookContext = deriveInboundMessageHookContext(ctx, { messageId: messageIdForHook }); const { isGroup, groupId } = hookContext; + let pluginClaimedInbound = false; if (hookRunner?.hasHooks("inbound_claim")) { const inboundClaim = await hookRunner.runInboundClaim( toPluginInboundClaimEvent(hookContext, { @@ -195,11 +196,7 @@ export async function dispatchReplyFromConfig(params: { }), toPluginInboundClaimContext(hookContext), ); - if (inboundClaim?.handled) { - markIdle("plugin_claim"); - recordProcessed("completed", { reason: "plugin-claimed" }); - return { queuedFinal: false, counts: dispatcher.getQueuedCounts() }; - } + pluginClaimedInbound = inboundClaim?.handled === true; } // Trigger plugin hooks (fire-and-forget) @@ -226,6 +223,12 @@ export async function dispatchReplyFromConfig(params: { ); } + if (pluginClaimedInbound) { + markIdle("plugin_claim"); + recordProcessed("completed", { reason: "plugin-claimed" }); + return { queuedFinal: false, counts: dispatcher.getQueuedCounts() }; + } + // Check if we should route replies to originating channel instead of dispatcher. // Only route when the originating channel is DIFFERENT from the current surface. // This handles cross-provider routing (e.g., message from Telegram being processed