fix(telegram): route native topic commands to the active session (#38871)

* fix(telegram): resolve session entry for /stop in forum topics

Fixes #38675

- Export normalizeStoreSessionKey from store.ts for reuse
- Use it in resolveSessionEntryForKey so topic session keys (lowercase
  in store) are found when handling /stop
- Add test for forum topic session key lookup

* fix(telegram): share native topic routing with inbound messages

* fix: land telegram topic routing follow-up (#38871)

---------

Co-authored-by: xialonglee <li.xialong@xydigit.com>
This commit is contained in:
Ayaan Zaidi
2026-03-07 19:01:16 +05:30
committed by GitHub
parent bfc36cc86d
commit 9e1de97a69
13 changed files with 335 additions and 172 deletions

View File

@@ -1,8 +1,5 @@
import type { Bot, Context } from "grammy";
import {
ensureConfiguredAcpRouteReady,
resolveConfiguredAcpRoute,
} from "../acp/persistent-bindings.route.js";
import { ensureConfiguredAcpRouteReady } from "../acp/persistent-bindings.route.js";
import { resolveChunkMode } from "../auto-reply/chunk.js";
import type { CommandArgs } from "../auto-reply/commands-registry.js";
import {
@@ -60,12 +57,11 @@ import {
buildTelegramThreadParams,
buildSenderName,
buildTelegramGroupFrom,
buildTelegramGroupPeerId,
buildTelegramParentPeer,
resolveTelegramGroupAllowFromContext,
resolveTelegramThreadSpec,
} from "./bot/helpers.js";
import type { TelegramContext } from "./bot/types.js";
import { resolveTelegramConversationRoute } from "./conversation-route.js";
import {
evaluateTelegramGroupBaseAccess,
evaluateTelegramGroupPolicyAccess,
@@ -424,15 +420,17 @@ export const registerTelegramNativeCommands = ({
isGroup: boolean;
isForum: boolean;
resolvedThreadId?: number;
senderId?: string;
topicAgentId?: string;
}): Promise<{
chatId: number;
threadSpec: ReturnType<typeof resolveTelegramThreadSpec>;
route: ReturnType<typeof resolveAgentRoute>;
route: ReturnType<typeof resolveTelegramConversationRoute>["route"];
mediaLocalRoots: readonly string[] | undefined;
tableMode: ReturnType<typeof resolveMarkdownTableMode>;
chunkMode: ReturnType<typeof resolveChunkMode>;
} | null> => {
const { msg, isGroup, isForum, resolvedThreadId } = params;
const { msg, isGroup, isForum, resolvedThreadId, senderId, topicAgentId } = params;
const chatId = msg.chat.id;
const messageThreadId = (msg as { message_thread_id?: number }).message_thread_id;
const threadSpec = resolveTelegramThreadSpec({
@@ -440,28 +438,16 @@ export const registerTelegramNativeCommands = ({
isForum,
messageThreadId,
});
const parentPeer = buildTelegramParentPeer({ isGroup, resolvedThreadId, chatId });
const peerId = isGroup ? buildTelegramGroupPeerId(chatId, resolvedThreadId) : String(chatId);
let route = resolveAgentRoute({
let { route, configuredBinding } = resolveTelegramConversationRoute({
cfg,
channel: "telegram",
accountId,
peer: {
kind: isGroup ? "group" : "direct",
id: peerId,
},
parentPeer,
chatId,
isGroup,
resolvedThreadId,
replyThreadId: threadSpec.id,
senderId,
topicAgentId,
});
const configuredRoute = resolveConfiguredAcpRoute({
cfg,
route,
channel: "telegram",
accountId,
conversationId: peerId,
parentConversationId: isGroup ? String(chatId) : undefined,
});
const configuredBinding = configuredRoute.configuredBinding;
route = configuredRoute.route;
if (configuredBinding) {
const ensured = await ensureConfiguredAcpRouteReady({
cfg,
@@ -562,6 +548,8 @@ export const registerTelegramNativeCommands = ({
isGroup,
isForum,
resolvedThreadId,
senderId,
topicAgentId: topicConfig?.agentId,
});
if (!runtimeContext) {
return;
@@ -788,6 +776,8 @@ export const registerTelegramNativeCommands = ({
isGroup,
isForum,
resolvedThreadId,
senderId,
topicAgentId: auth.topicConfig?.agentId,
});
if (!runtimeContext) {
return;