UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

7,881 lines 317 kB
import { a as normalizeLowercaseStringOrEmpty, c as normalizeOptionalString, s as normalizeOptionalLowercaseString } from "./string-coerce-mnp54Vah.js";
import { C as resolveExpiresAtMsFromDurationMs, P as timestampMsToIsoString, b as parseStrictPositiveInteger, m as isFutureDateTimestampMs, o as asDateTimestampMs } from "./number-coercion-CJQ8TR--.js";
import { a as formatUncaughtError, i as formatErrorMessage } from "./errors-BXgSefBE.js";
import { _ as uniqueStrings, l as normalizeStringEntries } from "./string-normalization-WNUDCpXX.js";
import { t as createNonExitingRuntime } from "./runtime-B4lgFmsS.js";
import { r as getChildLogger } from "./logger-Brhy0cvC.js";
import { t as createSubsystemLogger } from "./subsystem-BzXSmsuh.js";
import { n as normalizeAccountId } from "./account-id-Df9e41E6.js";
import { h as sanitizeAgentId, i as buildAgentMainSessionKey, m as resolveThreadSessionKeys } from "./session-key-B_NoIfpX.js";
import { a as resolveAgentDir, c as resolveDefaultAgentId, r as resolveAgentConfig } from "./agent-scope-config-CgCYpZfK.js";
import { a as shouldLogVerbose, r as logVerbose, s as warn, t as danger } from "./globals-GTrXU4s9.js";
import { i as getRuntimeConfig } from "./io-Gi7-pyU-.js";
import { i as getRuntimeConfigSnapshot } from "./runtime-snapshot-D93_HOsR.js";
import { n as mutateConfigFile } from "./config-C9RxTsn1.js";
import { a as getReplyPayloadTtsSupplement, n as buildTtsSupplementMediaPayload, o as isReplyPayloadNonTerminalToolErrorWarning } from "./reply-payload-CZJ2cgZc.js";
import { u as fireAndForgetHook } from "./hook-runner-global-BnyQREr6.js";
import { t as createDedupeCache } from "./dedupe-DnzL4okR.js";
import { m as triggerInternalHook, n as createInternalHookEvent } from "./internal-hooks-Bq6_9FFu.js";
import { s as resolveSessionTranscriptPathInDir, u as resolveStorePath } from "./paths-NEwU8m3X.js";
import { V as resolveSessionStoreEntry } from "./store-load-C4uq6Lrq.js";
import { d as updateSessionStore, f as updateSessionStoreEntry } from "./store-Qsgtu-0y.js";
import { d as resolveAndPersistSessionFile, r as readLatestAssistantTextFromSessionTranscript, s as appendSessionTranscriptMessage } from "./transcript-Cw-EfeYD.js";
import { t as emitSessionTranscriptUpdate } from "./transcript-events-DTn-thXR.js";
import { x as findModelInCatalog } from "./model-selection-shared-b32cUYts.js";
import { n as resolveThinkingDefaultWithRuntimeCatalog } from "./model-thinking-default-dIc9T64G.js";
import { c as resolveDefaultModelForAgent } from "./model-selection-CA_65iXi.js";
import { i as modelSupportsVision, n as loadModelCatalog } from "./model-catalog-0-HKr2yW.js";
import { i as isExplicitCommandTurn } from "./command-turn-context-YXLLZVCi.js";
import { n as listChatCommands } from "./commands-registry-list-MVYs4B-p.js";
import { n as maybeResolveTextAlias, r as normalizeCommandBody } from "./commands-registry-normalize-BwQxckuw.js";
import { n as isAbortRequestText } from "./abort-primitives-CNNnD8oN.js";
import { t as hasControlCommand } from "./command-detection-iKcBJ8it.js";
import { d as stripReasoningTagsFromText, g as isInsideCode, h as findCodeRegions } from "./assistant-visible-text-QdHceFYu.js";
import { n as sleepWithAbort, t as computeBackoff } from "./backoff-S1yazmy1.js";
import { i as toInternalMessageReceivedContext } from "./message-hook-mappers-BqsqHKq-.js";
import { m as resolveSendableOutboundReplyParts } from "./reply-payload-D-VMfpYI.js";
import { l as normalizeMessagePresentation } from "./payload-CZlThETZ.js";
import { n as resolveChannelGroupRequireMention, t as resolveChannelGroupPolicy } from "./group-policy-CShy7eWv.js";
import { i as resolveAgentRoute, n as deriveLastRoutePolicy, t as buildAgentSessionKey } from "./resolve-route-xcIS8NFL.js";
import { n as firstDefined } from "./allow-from-Ddq5GeWG.js";
import { n as MediaFetchError } from "./fetch-2npQWHQh.js";
import { r as getAgentScopedMediaLocalRoots } from "./local-roots-BqdtViGS.js";
import { t as resolveAckReaction } from "./identity-Qcvvo6SY.js";
import { t as deriveDurableFinalDeliveryRequirements } from "./capabilities-tytC94t4.js";
import { D as resolveChannelStreamingPreviewNativeToolProgress, M as resolveTranscriptBackedChannelFinalText, N as selectLongerFinalText, O as resolveChannelStreamingPreviewNativeToolProgressAllowFrom, S as resolveChannelStreamingBlockEnabled, d as isPotentialTruncatedFinal, i as buildChannelProgressDraftLineForEntry, k as resolveChannelStreamingPreviewToolProgress, o as formatChannelProgressDraftLine, s as formatChannelProgressDraftLineForEntry } from "./streaming-_TVeMLGC.js";
import { c as resolveTextChunkLimit, i as chunkMarkdownTextWithMode, s as resolveChunkMode } from "./chunk-Cpb8JO1x.js";
import { a as projectOutboundPayloadPlanForDelivery, t as createOutboundPayloadPlan } from "./payloads-B4DTFTLz.js";
import { n as createPreviewMessageReceipt } from "./live-DjttXqqq.js";
import { h as resolveChannelConfigWrites } from "./channel-config-helpers-lj5quus3.js";
import { s as formatReasoningMessage } from "./embedded-agent-utils-CUYamrTB.js";
import { t as applyModelOverrideToSessionEntry } from "./model-overrides-D5WaLvcp.js";
import { c as resolveThreadBindingMaxAgeMsForChannel, o as resolveThreadBindingIdleTimeoutMsForChannel, u as resolveThreadBindingSpawnPolicy } from "./thread-bindings-policy-DZGWBoLX.js";
import { t as isApprovalNotFoundError } from "./approval-errors-Cl3t2MJo.js";
import "./error-runtime-C8vbtAJt.js";
import "./media-runtime-fgpmX6eL.js";
import "./number-runtime-DBLVDypr.js";
import "./runtime-config-snapshot-CDoFLjqb.js";
import "./runtime-env-D_2q8-VK.js";
import "./security-runtime-CQm7DD1u.js";
import { t as evaluateSupplementalContextVisibility } from "./context-visibility-C5CaKMWO.js";
import "./string-coerce-runtime-CEGJWkQ_.js";
import "./text-chunking-D45TeeEs.js";
import { f as parsePluginBindingApprovalCustomId, i as buildPluginBindingResolvedText, m as resolvePluginConversationBindingApproval } from "./conversation-binding-F1u3otwq.js";
import { r as getPluginCommandSpecs } from "./command-specs-DjCWwpsp.js";
import { c as parseCommandArgs, i as formatCommandArgMenuTitle, n as buildCommandTextFromArgs, o as listNativeCommandSpecs, r as findCommandByNativeName, s as listNativeCommandSpecsForConfig, u as resolveCommandArgMenu } from "./commands-registry-wvHlpYBK.js";
import { i as matchesMentionWithExplicit, n as buildMentionRegexes, t as CURRENT_MESSAGE_MARKER } from "./mentions-Be0DZkGs.js";
import { n as resolveStoredModelOverride } from "./stored-model-override-B3aWM_od.js";
import { o as normalizeTelegramApiRoot, r as resolveTelegramTransport } from "./fetch-DVxN6WLs.js";
import "./routing-CQ63ICPX.js";
import { a as resolveEnvelopeFormatOptions, r as formatInboundEnvelope } from "./envelope-IMcsY0tl.js";
import { n as resolveInboundDebounceMs, t as createInboundDebouncer } from "./inbound-debounce-k9j7XKN1.js";
import { i as shouldAckReaction, n as removeAckReactionAfterReply } from "./ack-reactions-D4-6N7fi.js";
import { n as resolveInboundMentionDecision, t as implicitMentionKindWhen } from "./mention-gating-3P8aSD7o.js";
import "./history-6XlO5h6s.js";
import { t as createChannelReplyPipeline } from "./reply-pipeline-eJPstCAz.js";
import { t as createChannelHistoryWindow } from "./history-window-B0hyPVKC.js";
import { t as resolveMarkdownTableMode } from "./markdown-tables-LfSv7QSx.js";
import { a as readChannelAllowFromStore, d as upsertChannelPairingRequest } from "./pairing-store-CxANz_ON.js";
import "./channel-policy-CIW58SA5.js";
import "./reply-history-Y9HXucO9.js";
import { m as parseExecApprovalCommandText } from "./exec-approval-reply-BXMsffTv.js";
import "./approval-reply-runtime-CtMUsKCW.js";
import { t as resolveChannelContextVisibilityMode } from "./context-visibility-D4NM8ac1.js";
import { n as resolveNativeCommandsEnabled, r as resolveNativeSkillsEnabled, t as isNativeCommandsExplicitlyDisabled } from "./commands-0zpR1Wsm.js";
import "./config-mutation-B38RpU_9.js";
import { t as listSkillCommandsForAgents } from "./chat-commands-DKDF6ilZ.js";
import { n as isBtwRequestText } from "./btw-command-BUuUu5fh.js";
import { t as generateConversationLabel } from "./conversation-label-generator-C7h5orEd.js";
import { t as dispatchReplyWithBufferedBlockDispatcher } from "./reply-dispatch-runtime-B3glKPcA.js";
import "./reply-chunking-C9pgDFVG.js";
import { f as runChannelInboundEvent } from "./inbound-reply-dispatch-BpzI7GIp.js";
import { a as resolveChannelDraftStreamingChunking, c as createMessageReceiveContext, i as createChannelProgressDraftCompositor } from "./channel-outbound-B3_Zy-kG.js";
import { n as logInboundDrop, r as logTypingFailure, t as logAckFailure } from "./logging-gUWPKC5g.js";
import "./dedupe-runtime-D625pkUp.js";
import "./model-session-runtime-XUhYGuhe.js";
import { n as resolveConfiguredBindingRoute, r as resolveRuntimeConversationBindingRoute } from "./binding-routing-C-pxzG2P.js";
import "./conversation-runtime-VjM1-D7a.js";
import "./agent-runtime-CvpQRNVf.js";
import { i as createInteractiveConversationBindingHelpers, r as dispatchPluginInteractiveHandler } from "./plugin-runtime-Dw_Zc32A.js";
import "./agent-harness-runtime-zRYzuZJP.js";
import "./hook-runtime-CA9YrfdF.js";
import "./markdown-table-runtime-j_C4iTF_.js";
import { n as buildCommandsMessagePaginated } from "./command-status-builders-VFpUpet0.js";
import { t as resolveNativeCommandSessionTargets } from "./native-command-session-targets-C-zj9Kp-.js";
import { n as formatModelsAvailableHeader } from "./commands-models-BhAJq6Rj.js";
import "./command-auth-native-D4ML0UKp.js";
import "./command-primitives-runtime-BC_tCClg.js";
import "./command-status-BS3Anz73.js";
import "./command-detection-C5wNjT-o.js";
import "./command-surface-C3BkEX9f.js";
import { i as resolveUnmentionedGroupInboundPolicy, l as shouldDebounceTextInbound, o as formatLocationText, r as classifyChannelInboundEvent, s as toLocationContext } from "./channel-inbound-bl7VmTdr.js";
import { a as DEFAULT_TIMING, i as DEFAULT_EMOJIS } from "./channel-feedback-Bi1IYmE_.js";
import "./channel-mention-gating-DLhfnQDE.js";
import { n as createChannelIngressResolver, o as defineStableChannelIngressIdentity } from "./message-access-mwxdTUGC.js";
import "./channel-ingress-runtime-rGltVkKC.js";
import { t as createChannelPairingChallengeIssuer } from "./channel-pairing-Bk6_JhTm.js";
import { t as loadSessionStore } from "./session-store-runtime-rvUx2Fil.js";
import "./models-provider-runtime-wrU609z3.js";
import "./skill-commands-runtime-CejwLNRH.js";
import "./native-command-config-runtime-Dla2pZe-.js";
import { a as resolveDefaultTelegramAccountId, o as resolveTelegramAccount, s as resolveTelegramMediaRuntimeOptions } from "./accounts-blCxAL9j.js";
import { r as resolveTelegramInlineButtonsScope } from "./inline-buttons-4xnM1eKI.js";
import { f as shouldSuppressLocalTelegramExecApprovalPrompt, l as shouldEnableTelegramExecApprovalButtons, n as isTelegramExecApprovalApprover, r as isTelegramExecApprovalAuthorizedSender } from "./exec-approvals-CgxkwQwD.js";
import { $ as isSenderAllowed, A as resolveTelegramCommandAuthorization, B as withResolvedTelegramForumFlag, C as buildTypingThreadParams, D as loadTelegramPairingStoreIfNeeded, E as isTelegramCommandsAllowFromConfigured, F as resolveTelegramMessageForumFlagHint, G as hasBotMention, H as buildSenderName, I as resolveTelegramReplyId, J as renderTelegramTextEntities, L as resolveTelegramStreamMode, M as resolveTelegramForumFlag, N as resolveTelegramForumThreadId, P as resolveTelegramGroupAllowFromContext, Q as resolveTelegramDmAllow, R as resolveTelegramThreadSpec, S as buildTelegramThreadParams, T as extractTelegramForumFlag, U as extractTelegramLocation, V as buildSenderLabel, W as getTelegramTextParts, X as resolveTelegramPrimaryMedia, Z as expandTelegramAllowFromWithAccessGroups, _ as buildTelegramGroupFrom, b as buildTelegramParentPeer, c as buildTelegramConversationContext, et as normalizeAllowFrom, g as buildGroupLabel, h as TelegramPairingStoreReadError, i as recordSentMessage, j as resolveTelegramDirectPeerId, k as resolveTelegramBotHasTopicsEnabled, l as buildTelegramReplyChain, m as resolveTelegramMessageCacheScope, nt as resolveTelegramEffectiveDmPolicy, q as normalizeForwardedContext, tt as normalizeDmAllowFromWithStore, u as createTelegramMessageCache, v as buildTelegramGroupPeerId, w as describeReplyTarget, x as buildTelegramRoutingTarget, y as buildTelegramInboundOriginTarget, z as shouldUseTelegramDmThreadSession } from "./sent-message-cache-IpiNMjf9.js";
import { i as parseTelegramOpaqueCallbackData, r as parseTelegramNativeCommandCallbackData, t as buildTelegramNativeCommandCallbackData } from "./native-command-callback-data-Cmy0Yrg1.js";
import { t as resolveTelegramInlineButtons } from "./button-types-Di5CvasV.js";
import { a as renderTelegramHtmlText, n as markdownToTelegramChunks } from "./format-RoDvU0eA.js";
import { t as TELEGRAM_TEXT_CHUNK_LIMIT } from "./outbound-adapter-DltSQb_u.js";
import { d as buildModelsKeyboard, g as resolveModelSelection, h as parseModelCallbackData, m as getModelsPageSize, o as buildTelegramModelsMenuButtons, p as calculateTotalPages, t as buildCommandsPaginationKeyboard } from "./command-ui-CF0gmLbR.js";
import { i as resolveTelegramCustomCommands, r as normalizeTelegramCommandName, t as TELEGRAM_COMMAND_NAME_PATTERN } from "./command-config-CnxQsFly.js";
import { c as commitTelegramMessageDispatchReplay, l as createTelegramMessageDispatchReplayGuard, s as claimTelegramMessageDispatchReplay, u as releaseTelegramMessageDispatchReplay } from "./message-dispatch-dedupe-COAultHH.js";
import { r as cacheSticker } from "./sticker-cache-store-mh6jiBwt.js";
import { r as createTelegramThreadBindingManager } from "./thread-bindings-B0soi6Nv.js";
import { n as getTopicName, o as resolveTopicNameCacheScope, s as updateTopicName } from "./topic-name-cache-B2kWKFCe.js";
import { t as describeStickerImage } from "./sticker-cache-BFNVd7JL.js";
import { n as evaluateTelegramGroupPolicyAccess, t as evaluateTelegramGroupBaseAccess } from "./group-access-B5I9myIg.js";
import { A as apiThrottler, C as resolveTelegramClientTimeoutMinimumSeconds, D as withTelegramApiErrorLogging, O as getOrCreateAccountThrottler, S as createTelegramClientFetch, T as resolveTelegramOutboundClientTimeoutFloorSeconds, a as editMessageTelegram, b as buildInlineKeyboard, j as sequentialize, k as Bot, w as resolveTelegramClientTimeoutSeconds, x as asTelegramClientFetch, y as recordOutboundMessageForPromptContext } from "./send-f0zZCDGg.js";
import { t as beginTelegramInboundEventDeliveryCorrelation } from "./inbound-event-delivery-CNR-dgbp.js";
import { a as buildCappedTelegramMenuCommands, i as createTelegramDraftStream, n as createNativeTelegramToolProgressDraft, o as buildPluginTelegramMenuCommands, r as resolveTelegramExecApproval, s as syncTelegramMenuCommands, t as defaultTelegramBotDeps } from "./bot-deps-BbrXdVWs.js";
import { n as deliverReplies, r as emitInternalMessageSentHook, t as resolveMedia } from "./delivery-5Fl83GEE.js";
import path from "node:path";
import { randomUUID } from "node:crypto";
//#region extensions/telegram/src/bot-handlers.debounce-key.ts
function buildTelegramInboundDebounceKey(params) {
	return `telegram:${params.accountId?.trim() || "default"}:${params.conversationKey}:${params.senderId}:${params.debounceLane}`;
}
function buildTelegramInboundDebounceConversationKey(params) {
	return params.threadId != null ? `${params.chatId}:topic:${params.threadId}` : String(params.chatId);
}
//#endregion
//#region extensions/telegram/src/bot-handlers.media.ts
function isMediaSizeLimitError(err) {
	const errMsg = String(err);
	return errMsg.includes("exceeds") && errMsg.includes("MB limit");
}
function isRecoverableMediaGroupError(err) {
	return err instanceof MediaFetchError || isMediaSizeLimitError(err);
}
function hasInboundMedia(msg) {
	return Boolean(msg.media_group_id) || Array.isArray(msg.photo) && msg.photo.length > 0 || Boolean(msg.video ?? msg.video_note ?? msg.document ?? msg.audio ?? msg.voice ?? msg.sticker);
}
function resolveInboundMediaFileId(msg) {
	return msg.sticker?.file_id ?? msg.photo?.[msg.photo.length - 1]?.file_id ?? msg.video?.file_id ?? msg.video_note?.file_id ?? msg.document?.file_id ?? msg.audio?.file_id ?? msg.voice?.file_id;
}
//#endregion
//#region extensions/telegram/src/bot-native-command-deps.runtime.ts
let telegramSendRuntimePromise;
async function loadTelegramSendRuntime() {
	telegramSendRuntimePromise ??= import("./send-BpmZU7lt.js");
	return await telegramSendRuntimePromise;
}
const defaultTelegramNativeCommandDeps = {
	get getRuntimeConfig() {
		return getRuntimeConfig;
	},
	get readChannelAllowFromStore() {
		return readChannelAllowFromStore;
	},
	get dispatchReplyWithBufferedBlockDispatcher() {
		return dispatchReplyWithBufferedBlockDispatcher;
	},
	get listSkillCommandsForAgents() {
		return listSkillCommandsForAgents;
	},
	get syncTelegramMenuCommands() {
		return syncTelegramMenuCommands;
	},
	get getPluginCommandSpecs() {
		return getPluginCommandSpecs;
	},
	async editMessageTelegram(...args) {
		const { editMessageTelegram } = await loadTelegramSendRuntime();
		return await editMessageTelegram(...args);
	}
};
//#endregion
//#region extensions/telegram/src/conversation-route.ts
function resolveTelegramConversationRoute(params) {
	const peerId = params.isGroup ? buildTelegramGroupPeerId(params.chatId, params.resolvedThreadId) : resolveTelegramDirectPeerId({
		chatId: params.chatId,
		senderId: params.senderId
	});
	const parentPeer = buildTelegramParentPeer({
		isGroup: params.isGroup,
		resolvedThreadId: params.resolvedThreadId,
		chatId: params.chatId
	});
	let route = resolveAgentRoute({
		cfg: params.cfg,
		channel: "telegram",
		accountId: params.accountId,
		peer: {
			kind: params.isGroup ? "group" : "direct",
			id: peerId
		},
		parentPeer
	});
	const rawTopicAgentId = params.topicAgentId?.trim();
	if (rawTopicAgentId) {
		const topicAgentId = sanitizeAgentId(rawTopicAgentId);
		const sessionKey = normalizeLowercaseStringOrEmpty(buildAgentSessionKey({
			agentId: topicAgentId,
			channel: "telegram",
			accountId: params.accountId,
			peer: {
				kind: params.isGroup ? "group" : "direct",
				id: peerId
			},
			dmScope: params.cfg.session?.dmScope,
			identityLinks: params.cfg.session?.identityLinks
		}));
		const mainSessionKey = normalizeLowercaseStringOrEmpty(buildAgentMainSessionKey({ agentId: topicAgentId }));
		route = {
			...route,
			agentId: topicAgentId,
			sessionKey,
			mainSessionKey,
			lastRoutePolicy: deriveLastRoutePolicy({
				sessionKey,
				mainSessionKey
			})
		};
		logVerbose(`telegram: topic route override: topic=${params.resolvedThreadId ?? params.replyThreadId} agent=${topicAgentId} sessionKey=${route.sessionKey}`);
	}
	const configuredRoute = resolveConfiguredBindingRoute({
		cfg: params.cfg,
		route,
		conversation: {
			channel: "telegram",
			accountId: params.accountId,
			conversationId: peerId,
			parentConversationId: params.isGroup ? String(params.chatId) : void 0
		}
	});
	route = configuredRoute.route;
	let bindingMode = configuredRoute.bindingResolution ? {
		kind: "configured",
		binding: configuredRoute.bindingResolution,
		sessionKey: configuredRoute.boundSessionKey ?? route.sessionKey
	} : { kind: "none" };
	const runtimeBindingConversationId = params.replyThreadId != null ? `${params.chatId}:topic:${params.replyThreadId}` : String(params.chatId);
	const runtimeRoute = resolveRuntimeConversationBindingRoute({
		route,
		conversation: {
			channel: "telegram",
			accountId: params.accountId,
			conversationId: runtimeBindingConversationId
		}
	});
	route = runtimeRoute.route;
	if (runtimeRoute.bindingRecord) {
		bindingMode = runtimeRoute.boundSessionKey ? {
			kind: "runtime-bound",
			sessionKey: runtimeRoute.boundSessionKey
		} : { kind: "plugin-owned-runtime" };
		logVerbose(runtimeRoute.boundSessionKey ? `telegram: routed via bound conversation ${runtimeBindingConversationId} -> ${runtimeRoute.boundSessionKey}` : `telegram: plugin-bound conversation ${runtimeBindingConversationId}`);
	}
	return {
		route,
		bindingMode
	};
}
function resolveTelegramConversationBaseSessionKey(params) {
	if (!(normalizeAccountId(params.route.accountId) !== normalizeAccountId(resolveDefaultTelegramAccountId(params.cfg)) && params.route.matchedBy === "default") || params.isGroup) return params.route.sessionKey;
	return normalizeLowercaseStringOrEmpty(buildAgentSessionKey({
		agentId: params.route.agentId,
		channel: "telegram",
		accountId: params.route.accountId,
		peer: {
			kind: "direct",
			id: resolveTelegramDirectPeerId({
				chatId: params.chatId,
				senderId: params.senderId
			})
		},
		dmScope: "per-account-channel-peer",
		identityLinks: params.cfg.session?.identityLinks
	}));
}
//#endregion
//#region extensions/telegram/src/group-config-helpers.ts
function resolveTelegramGroupPromptSettings(params) {
	const skillFilter = firstDefined(params.topicConfig?.skills, params.groupConfig?.skills);
	const systemPromptParts = [params.groupConfig?.systemPrompt?.trim() || null, params.topicConfig?.systemPrompt?.trim() || null].filter((entry) => Boolean(entry));
	return {
		skillFilter,
		groupSystemPrompt: systemPromptParts.length > 0 ? systemPromptParts.join("\n\n") : void 0
	};
}
//#endregion
//#region extensions/telegram/src/ingress.ts
const TELEGRAM_CHANNEL_ID = "telegram";
const telegramIngressIdentity = defineStableChannelIngressIdentity({
	key: "telegram-user-id",
	normalize: (value) => {
		const normalized = normalizeAllowFrom([value]);
		return normalized.entries[0] ?? (normalized.hasWildcard ? "*" : null);
	},
	sensitivity: "pii"
});
function createTelegramIngressSubject(senderId) {
	return { stableId: senderId };
}
function createTelegramIngressResolver(params) {
	return createChannelIngressResolver({
		channelId: TELEGRAM_CHANNEL_ID,
		accountId: params.accountId ?? "default",
		identity: telegramIngressIdentity,
		cfg: params.cfg
	});
}
function telegramAllowEntries(allow) {
	return [...allow.hasWildcard ? ["*"] : [], ...allow.entries];
}
function telegramConversation(params) {
	return {
		kind: params.isGroup ? "group" : "direct",
		id: String(params.chatId),
		...params.resolvedThreadId != null ? { threadId: String(params.resolvedThreadId) } : {}
	};
}
async function resolveTelegramCommandIngressAuthorization(params) {
	const commandOwner = [...params.isGroup && params.includeDmAllowForGroupCommands === false ? [] : telegramAllowEntries(params.effectiveDmAllow), ...params.ownerAccess.senderIsOwner ? [params.senderId || "*"] : params.ownerAccess.ownerList];
	return (await createTelegramIngressResolver({
		accountId: params.accountId,
		cfg: params.cfg
	}).command({
		subject: createTelegramIngressSubject(params.senderId),
		conversation: telegramConversation(params),
		event: { kind: params.eventKind ?? "native-command" },
		dmPolicy: params.dmPolicy,
		groupPolicy: "allowlist",
		allowFrom: commandOwner,
		groupAllowFrom: params.isGroup ? telegramAllowEntries(params.effectiveGroupAllow) : [],
		command: {
			allowTextCommands: params.allowTextCommands ?? false,
			hasControlCommand: params.hasControlCommand ?? false,
			modeWhenAccessGroupsOff: params.modeWhenAccessGroupsOff ?? "configured"
		}
	})).commandAccess;
}
async function resolveTelegramEventIngressAuthorization(params) {
	return (await createTelegramIngressResolver({ accountId: params.accountId }).event({
		subject: createTelegramIngressSubject(params.senderId),
		conversation: telegramConversation(params),
		event: {
			kind: params.eventKind,
			authMode: "inbound"
		},
		dmPolicy: params.dmPolicy,
		groupPolicy: params.enforceGroupAuthorization ? "allowlist" : "open",
		allowFrom: telegramAllowEntries(params.effectiveDmAllow),
		groupAllowFrom: params.enforceGroupAuthorization ? telegramAllowEntries(params.effectiveGroupAllow) : []
	})).ingress;
}
//#endregion
//#region extensions/telegram/src/bot-native-commands.ts
const EMPTY_RESPONSE_FALLBACK$1 = "No response generated. Please try again.";
let telegramNativeCommandDeliveryRuntimePromise;
async function loadTelegramNativeCommandDeliveryRuntime() {
	telegramNativeCommandDeliveryRuntimePromise ??= import("./bot-native-commands.delivery.runtime.js");
	return await telegramNativeCommandDeliveryRuntimePromise;
}
let telegramNativeCommandRuntimePromise;
async function loadTelegramNativeCommandRuntime() {
	telegramNativeCommandRuntimePromise ??= import("./bot-native-commands.runtime.js");
	return await telegramNativeCommandRuntimePromise;
}
function resolveTelegramProgressPlaceholder(command) {
	const text = command.nativeProgressMessages?.telegram?.trim() ?? command.nativeProgressMessages?.default?.trim();
	return text ? text : null;
}
async function resolveTelegramCommandSessionFile(params) {
	const sessionKey = params.sessionKey.trim();
	if (!sessionKey) return {};
	try {
		const storePath = resolveStorePath(params.cfg.session?.store, { agentId: params.agentId });
		const store = loadSessionStore(storePath);
		const resolved = resolveSessionStoreEntry({
			store,
			sessionKey
		});
		const sessionId = resolved.existing?.sessionId?.trim() || randomUUID();
		const authProfileId = normalizeOptionalString(resolved.existing?.authProfileOverride);
		const sessionsDir = path.dirname(storePath);
		const fallbackSessionFile = resolveSessionTranscriptPathInDir(sessionId, sessionsDir, params.threadId);
		return {
			sessionId,
			sessionFile: (await resolveAndPersistSessionFile({
				sessionId,
				sessionKey: resolved.normalizedKey,
				sessionStore: store,
				storePath,
				sessionEntry: resolved.existing,
				agentId: params.agentId,
				sessionsDir,
				fallbackSessionFile
			})).sessionFile,
			...authProfileId ? { authProfileId } : {}
		};
	} catch {
		return {};
	}
}
function resolveTelegramCommandMenuModelContext(params) {
	if (!params.sessionKey.trim()) return {};
	try {
		const storePath = resolveStorePath(params.cfg.session?.store, { agentId: params.agentId });
		const defaultModel = resolveDefaultModelForAgent({
			cfg: params.cfg,
			agentId: params.agentId
		});
		const store = loadSessionStore(storePath);
		const entry = resolveSessionStoreEntry({
			store,
			sessionKey: params.sessionKey
		}).existing;
		const thinkingLevel = normalizeOptionalString(entry?.thinkingLevel);
		if (entry?.modelOverrideSource === "auto" && normalizeOptionalString(entry.modelOverride)) return {
			provider: defaultModel.provider,
			model: defaultModel.model,
			...thinkingLevel ? { thinkingLevel } : {}
		};
		const override = resolveStoredModelOverride({
			sessionEntry: entry,
			sessionStore: store,
			sessionKey: params.sessionKey,
			defaultProvider: defaultModel.provider
		});
		if (override?.model) return {
			provider: override.provider || defaultModel.provider,
			model: override.model,
			...thinkingLevel ? { thinkingLevel } : {}
		};
		const provider = normalizeOptionalString(entry?.providerOverride) ?? normalizeOptionalString(entry?.modelProvider);
		const model = normalizeOptionalString(entry?.modelOverride) ?? normalizeOptionalString(entry?.model);
		return {
			...provider ? { provider } : {},
			...model ? { model } : {},
			...thinkingLevel ? { thinkingLevel } : {}
		};
	} catch {
		return {};
	}
}
async function resolveTelegramDefaultThinkingLevel(params) {
	return resolveThinkingDefaultWithRuntimeCatalog({
		cfg: params.cfg,
		provider: params.provider,
		model: params.model,
		loadModelCatalog: () => loadModelCatalog({ config: params.cfg })
	});
}
async function resolveTelegramThinkMenuCurrentLevel(params) {
	const explicit = normalizeOptionalString(params.thinkingLevel);
	if (explicit) return explicit;
	const agentThinkingDefault = normalizeOptionalString(resolveAgentConfig(params.cfg, params.agentId)?.thinkingDefault);
	if (agentThinkingDefault) return agentThinkingDefault;
	const defaultModel = resolveDefaultModelForAgent({
		cfg: params.cfg,
		agentId: params.agentId
	});
	return await resolveTelegramDefaultThinkingLevel({
		cfg: params.cfg,
		provider: params.provider ?? defaultModel.provider,
		model: params.model ?? defaultModel.model
	});
}
function formatTelegramCommandArgMenuTitle(params) {
	const title = formatCommandArgMenuTitle({
		command: params.command,
		menu: params.menu
	});
	if (params.command.key !== "think" || !params.currentThinkingLevel) return title;
	return `Current thinking level: ${params.currentThinkingLevel}.\n${title}`;
}
function resolveTelegramNativeReplyChannelData(result) {
	return result.channelData?.telegram;
}
function normalizeTelegramNativeReplyPayload(result) {
	return result && typeof result === "object" ? result : {};
}
function hasRenderableTelegramNativeReplyPayload(result) {
	return resolveSendableOutboundReplyParts(result).hasContent;
}
function isEditableTelegramProgressResult(result) {
	const telegramData = resolveTelegramNativeReplyChannelData(result);
	return Boolean(typeof result.text === "string" && result.text.trim() && !result.mediaUrl && (!result.mediaUrls || result.mediaUrls.length === 0) && !result.presentation && !result.interactive && !result.btw && telegramData?.pin !== true);
}
async function cleanupTelegramProgressPlaceholder(params) {
	const progressMessageId = params.progressMessageId;
	if (progressMessageId == null) return;
	try {
		await withTelegramApiErrorLogging({
			operation: "deleteMessage",
			runtime: params.runtime,
			fn: () => params.bot.api.deleteMessage(params.chatId, progressMessageId)
		});
	} catch {}
}
async function resolveTelegramNativeCommandThreadContext(params) {
	const { msg, bot } = params;
	const chatId = msg.chat.id;
	const isGroup = msg.chat.type === "group" || msg.chat.type === "supergroup";
	const messageThreadId = msg.message_thread_id;
	const getChat = typeof bot.api.getChat === "function" ? bot.api.getChat.bind(bot.api) : void 0;
	const isForum = await resolveTelegramForumFlag({
		chatId,
		chatType: msg.chat.type,
		isGroup,
		isForum: extractTelegramForumFlag(msg.chat),
		isTopicMessage: msg.is_topic_message,
		getChat
	});
	const threadSpec = resolveTelegramThreadSpec({
		isGroup,
		isForum,
		messageThreadId
	});
	return {
		chatId,
		isGroup,
		isForum,
		messageThreadId,
		threadSpec,
		threadParams: buildTelegramThreadParams(threadSpec)
	};
}
function resolveTelegramNativeCommandDisableBlockStreaming(telegramCfg) {
	const blockStreamingEnabled = resolveChannelStreamingBlockEnabled(telegramCfg);
	return typeof blockStreamingEnabled === "boolean" ? !blockStreamingEnabled : void 0;
}
async function resolveTelegramCommandAuth(params) {
	const { msg, bot, cfg, accountId, telegramCfg, readChannelAllowFromStore, allowFrom, groupAllowFrom, useAccessGroups, resolveGroupPolicy, resolveTelegramGroupConfig, requireAuth } = params;
	const { chatId, isGroup, isForum, messageThreadId, threadParams } = await resolveTelegramNativeCommandThreadContext({
		msg,
		bot
	});
	const senderId = msg.from?.id ? String(msg.from.id) : "";
	const senderUsername = msg.from?.username ?? "";
	const commandsAllowFromConfigured = isTelegramCommandsAllowFromConfigured(cfg);
	const preContextCommandsAllowFromAccess = commandsAllowFromConfigured ? resolveTelegramCommandAuthorization({
		cfg,
		accountId,
		chatId,
		isGroup,
		senderId,
		senderUsername
	}) : null;
	const { resolvedThreadId, dmThreadId, storeAllowFrom, groupConfig, topicConfig, groupAllowOverride, effectiveGroupAllow, hasGroupAllowOverride } = await resolveTelegramGroupAllowFromContext({
		cfg,
		chatId,
		accountId,
		dmPolicy: telegramCfg.dmPolicy,
		allowFrom,
		senderId,
		isGroup,
		isForum,
		messageThreadId,
		groupAllowFrom,
		skipPairingStoreRead: Boolean(preContextCommandsAllowFromAccess?.isAuthorizedSender),
		readChannelAllowFromStore,
		resolveTelegramGroupConfig
	});
	const effectiveDmPolicy = resolveTelegramEffectiveDmPolicy({
		isGroup,
		groupConfig,
		dmPolicy: telegramCfg.dmPolicy
	});
	const requireTopic = !isGroup && groupConfig && "requireTopic" in groupConfig ? groupConfig.requireTopic : void 0;
	if (!isGroup && requireTopic === true && dmThreadId == null) {
		logVerbose(`Blocked telegram command in DM ${chatId}: requireTopic=true but no topic present`);
		return null;
	}
	const dmAllowFrom = groupAllowOverride ?? allowFrom;
	const commandsAllowFromAccess = commandsAllowFromConfigured ? resolveTelegramCommandAuthorization({
		cfg,
		accountId,
		chatId,
		isGroup,
		resolvedThreadId,
		senderId,
		senderUsername
	}) : null;
	const ownerAccess = resolveTelegramCommandAuthorization({
		cfg,
		accountId,
		chatId,
		isGroup,
		resolvedThreadId,
		senderId,
		senderUsername
	});
	const sendAuthMessage = async (text) => {
		await withTelegramApiErrorLogging({
			operation: "sendMessage",
			fn: () => bot.api.sendMessage(chatId, text, threadParams ?? {})
		});
		return null;
	};
	const rejectNotAuthorized = async () => {
		return await sendAuthMessage("You are not authorized to use this command.");
	};
	const baseAccess = evaluateTelegramGroupBaseAccess({
		isGroup,
		groupConfig,
		topicConfig,
		hasGroupAllowOverride,
		effectiveGroupAllow,
		senderId,
		senderUsername,
		enforceAllowOverride: requireAuth,
		requireSenderForAllowOverride: true
	});
	if (!baseAccess.allowed) {
		if (baseAccess.reason === "group-disabled") return await sendAuthMessage("This group is disabled.");
		if (baseAccess.reason === "topic-disabled") return await sendAuthMessage("This topic is disabled.");
		return await rejectNotAuthorized();
	}
	const policyAccess = evaluateTelegramGroupPolicyAccess({
		isGroup,
		chatId,
		cfg,
		telegramCfg,
		topicConfig,
		groupConfig,
		effectiveGroupAllow,
		senderId,
		senderUsername,
		resolveGroupPolicy,
		enforcePolicy: useAccessGroups,
		useTopicAndGroupOverrides: false,
		enforceAllowlistAuthorization: requireAuth && !commandsAllowFromConfigured,
		allowEmptyAllowlistEntries: true,
		requireSenderForAllowlistAuthorization: true,
		checkChatAllowlist: useAccessGroups
	});
	if (!policyAccess.allowed) {
		if (policyAccess.reason === "group-policy-disabled") return await sendAuthMessage("Telegram group commands are disabled.");
		if (policyAccess.reason === "group-policy-allowlist-no-sender" || policyAccess.reason === "group-policy-allowlist-unauthorized") return await rejectNotAuthorized();
		if (policyAccess.reason === "group-chat-not-allowed") return await sendAuthMessage("This group is not allowed.");
	}
	const dmAllow = normalizeDmAllowFromWithStore({
		allowFrom: await expandTelegramAllowFromWithAccessGroups({
			cfg,
			allowFrom: dmAllowFrom,
			accountId,
			senderId
		}),
		storeAllowFrom: isGroup ? [] : storeAllowFrom,
		dmPolicy: effectiveDmPolicy
	});
	const commandAuthorized = commandsAllowFromConfigured ? Boolean(commandsAllowFromAccess?.isAuthorizedSender) : (await resolveTelegramCommandIngressAuthorization({
		accountId,
		cfg,
		dmPolicy: effectiveDmPolicy,
		isGroup,
		chatId,
		resolvedThreadId,
		senderId,
		effectiveDmAllow: dmAllow,
		effectiveGroupAllow,
		ownerAccess,
		eventKind: "native-command"
	})).authorized;
	if (requireAuth && !commandAuthorized) return await rejectNotAuthorized();
	return {
		chatId,
		isGroup,
		isForum,
		resolvedThreadId,
		senderId,
		senderUsername,
		groupConfig,
		topicConfig,
		commandAuthorized,
		senderIsOwner: ownerAccess.senderIsOwner
	};
}
const registerTelegramNativeCommands = ({ bot, cfg, runtime, accountId, telegramCfg, allowFrom, groupAllowFrom, replyToMode, textLimit, mediaMaxBytes, useAccessGroups, nativeEnabled, nativeSkillsEnabled, nativeDisabledExplicit, resolveGroupPolicy, resolveTelegramGroupConfig, shouldSkipUpdate, telegramDeps = defaultTelegramNativeCommandDeps, opts }) => {
	const boundRoute = nativeEnabled && nativeSkillsEnabled ? resolveAgentRoute({
		cfg,
		channel: "telegram",
		accountId
	}) : null;
	if (nativeEnabled && nativeSkillsEnabled && !boundRoute) runtime.log?.("nativeSkillsEnabled is true but no agent route is bound for this Telegram account; skill commands will not appear in the native menu.");
	const skillCommands = nativeEnabled && nativeSkillsEnabled && boundRoute ? telegramDeps.listSkillCommandsForAgents({
		cfg,
		agentIds: [boundRoute.agentId]
	}) : [];
	const pluginCommandSpecs = (telegramDeps.getPluginCommandSpecs ?? defaultTelegramNativeCommandDeps.getPluginCommandSpecs)?.("telegram", { config: cfg }) ?? [];
	const resolveTelegramMenuCommandCatalog = (activeSkillCommands, reservedSkillCommands = activeSkillCommands) => {
		const nativeCommands = nativeEnabled ? listNativeCommandSpecsForConfig(cfg, {
			skillCommands: activeSkillCommands,
			provider: "telegram"
		}) : [];
		const reservedCommands = new Set(listNativeCommandSpecs().map((command) => normalizeTelegramCommandName(command.name)));
		for (const command of reservedSkillCommands) reservedCommands.add(normalizeLowercaseStringOrEmpty(command.name));
		const customResolution = resolveTelegramCustomCommands({
			commands: telegramCfg.customCommands,
			reservedCommands
		});
		for (const issue of customResolution.issues) runtime.error?.(danger(issue.message));
		const customCommands = customResolution.commands;
		const existingCommands = new Set([...nativeCommands.map((command) => normalizeTelegramCommandName(command.name)), ...customCommands.map((command) => command.command)].map((command) => normalizeLowercaseStringOrEmpty(command)));
		for (const command of reservedSkillCommands) existingCommands.add(normalizeTelegramCommandName(command.name));
		const pluginCatalog = buildPluginTelegramMenuCommands({
			specs: pluginCommandSpecs,
			existingCommands
		});
		for (const issue of pluginCatalog.issues) runtime.error?.(danger(issue));
		return {
			nativeCommands,
			customCommands,
			pluginCatalog,
			...buildCappedTelegramMenuCommands({ allCommands: [
				...nativeCommands.map((command) => {
					const normalized = normalizeTelegramCommandName(command.name);
					if (!TELEGRAM_COMMAND_NAME_PATTERN.test(normalized)) {
						runtime.error?.(danger(`Native command "${command.name}" is invalid for Telegram (resolved to "${normalized}"). Skipping.`));
						return null;
					}
					const menuCommand = {
						command: normalized,
						description: command.description
					};
					if (command.isAlias) menuCommand.isAlias = true;
					if (command.descriptionLocalizations) menuCommand.descriptionLocalizations = command.descriptionLocalizations;
					return menuCommand;
				}).filter((cmd) => cmd !== null),
				...nativeEnabled ? pluginCatalog.commands : [],
				...customCommands
			] })
		};
	};
	const fullCommandCatalog = resolveTelegramMenuCommandCatalog(skillCommands);
	let menuCommandCatalog = fullCommandCatalog;
	if (nativeEnabled && nativeSkillsEnabled && skillCommands.length > 0 && fullCommandCatalog.overflowCount > 0) {
		const initialCommandCount = fullCommandCatalog.totalCommands;
		menuCommandCatalog = resolveTelegramMenuCommandCatalog([], skillCommands);
		runtime.log?.(`Telegram: ${initialCommandCount} commands exceeds limit; removing per-skill commands and keeping /skill.`);
	}
	const { nativeCommands, pluginCatalog } = fullCommandCatalog;
	const loadFreshRuntimeConfig = () => telegramDeps.getRuntimeConfig();
	const resolveFreshTelegramConfig = (runtimeCfg) => {
		try {
			return resolveTelegramAccount({
				cfg: runtimeCfg,
				accountId
			}).config;
		} catch (error) {
			logVerbose(`telegram native command: failed to load fresh account config for ${accountId}; using startup snapshot: ${String(error)}`);
			return telegramCfg;
		}
	};
	const { commandsToRegister, totalCommands, maxCommands, overflowCount, maxTotalChars, descriptionTrimmed, textBudgetDropCount } = menuCommandCatalog;
	if (overflowCount > 0) runtime.log?.(`Telegram limits bots to ${maxCommands} commands. ${totalCommands} configured; registering first ${maxCommands}. Use channels.telegram.commands.native: false to disable, or reduce plugin/skill/custom commands.`);
	if (descriptionTrimmed) runtime.log?.(`Telegram menu text exceeded the conservative ${maxTotalChars}-character payload budget; shortening descriptions to keep ${commandsToRegister.length} commands visible.`);
	if (textBudgetDropCount > 0) runtime.log?.(`Telegram menu text still exceeded the conservative ${maxTotalChars}-character payload budget after shortening descriptions; registering first ${commandsToRegister.length} commands.`);
	(telegramDeps.syncTelegramMenuCommands ?? syncTelegramMenuCommands)({
		bot,
		runtime,
		commandsToRegister,
		accountId,
		botIdentity: opts.token
	});
	const resolveCommandRuntimeContext = async (params) => {
		const { msg, runtimeCfg, isGroup, isForum, resolvedThreadId, senderId, topicAgentId } = params;
		const chatId = msg.chat.id;
		const messageThreadId = msg.message_thread_id;
		const threadSpec = resolveTelegramThreadSpec({
			isGroup,
			isForum,
			messageThreadId: resolvedThreadId ?? messageThreadId
		});
		const { route, bindingMode } = resolveTelegramConversationRoute({
			cfg: runtimeCfg,
			accountId,
			chatId,
			isGroup,
			resolvedThreadId,
			replyThreadId: threadSpec.id,
			senderId,
			topicAgentId
		});
		const nativeCommandRuntime = await loadTelegramNativeCommandRuntime();
		if (bindingMode.kind === "configured") {
			const ensured = await nativeCommandRuntime.ensureConfiguredBindingRouteReady({
				cfg: runtimeCfg,
				bindingResolution: bindingMode.binding
			});
			if (!ensured.ok) {
				logVerbose(`telegram native command: configured ACP binding unavailable for topic ${bindingMode.binding.record.conversation.conversationId}: ${ensured.error}`);
				await withTelegramApiErrorLogging({
					operation: "sendMessage",
					runtime,
					fn: () => bot.api.sendMessage(chatId, "Configured ACP binding is unavailable right now. Please try again.", buildTelegramThreadParams(threadSpec) ?? {})
				});
				return null;
			}
		}
		return {
			chatId,
			threadSpec,
			route,
			mediaLocalRoots: nativeCommandRuntime.getAgentScopedMediaLocalRoots(runtimeCfg, route.agentId),
			tableMode: resolveMarkdownTableMode({
				cfg: runtimeCfg,
				channel: "telegram",
				accountId: route.accountId
			}),
			chunkMode: nativeCommandRuntime.resolveChunkMode(runtimeCfg, "telegram", route.accountId)
		};
	};
	const buildCommandDeliveryBaseOptions = (params) => ({
		cfg: params.cfg,
		chatId: String(params.chatId),
		accountId: params.accountId,
		sessionKeyForInternalHooks: params.sessionKeyForInternalHooks,
		policySessionKey: params.policySessionKey,
		mirrorIsGroup: params.mirrorIsGroup,
		mirrorGroupId: params.mirrorGroupId,
		token: opts.token,
		runtime,
		bot,
		mediaLocalRoots: params.mediaLocalRoots,
		mediaMaxBytes,
		replyToMode,
		textLimit,
		thread: params.threadSpec,
		tableMode: params.tableMode,
		chunkMode: params.chunkMode,
		linkPreview: params.linkPreview
	});
	const resolveCommandTargetSessionKey = (params) => {
		const baseSessionKey = resolveTelegramConversationBaseSessionKey({
			cfg: params.runtimeCfg,
			route: params.route,
			chatId: params.chatId,
			isGroup: params.isGroup,
			senderId: params.senderId
		});
		const dmThreadId = params.threadSpec.scope === "dm" ? params.threadSpec.id : void 0;
		return (shouldUseTelegramDmThreadSession({
			dmThreadId,
			botHasTopicsEnabled: params.botHasTopicsEnabled
		}) && dmThreadId != null ? params.resolveThreadSessionKeys({
			baseSessionKey,
			threadId: `${params.chatId}:${dmThreadId}`
		}) : null)?.sessionKey ?? baseSessionKey;
	};
	if (commandsToRegister.length > 0 || pluginCatalog.commands.length > 0) {
		for (const command of nativeCommands) {
			const normalizedCommandName = normalizeTelegramCommandName(command.name);
			bot.command(normalizedCommandName, async (ctx) => {
				const msg = ctx.message;
				if (!msg) return;
				if (shouldSkipUpdate(ctx)) return;
				const runtimeCfg = loadFreshRuntimeConfig();
				const runtimeTelegramCfg = resolveFreshTelegramConfig(runtimeCfg);
				const auth = await resolveTelegramCommandAuth({
					msg,
					bot,
					cfg: runtimeCfg,
					accountId,
					telegramCfg: runtimeTelegramCfg,
					readChannelAllowFromStore: telegramDeps.readChannelAllowFromStore,
					allowFrom,
					groupAllowFrom,
					useAccessGroups,
					resolveGroupPolicy,
					resolveTelegramGroupConfig,
					requireAuth: true
				});
				if (!auth) return;
				const { chatId, isGroup, isForum, resolvedThreadId, senderId, senderUsername, groupConfig, topicConfig, commandAuthorized } = auth;
				const runtimeContext = await resolveCommandRuntimeContext({
					msg,
					runtimeCfg,
					isGroup,
					isForum,
					resolvedThreadId,
					senderId,
					topicAgentId: topicConfig?.agentId
				});
				if (!runtimeContext) return;
				const { threadSpec, route, mediaLocalRoots, tableMode, chunkMode } = runtimeContext;
				const threadParams = buildTelegramThreadParams(threadSpec) ?? {};
				const originatingTo = buildTelegramRoutingTarget(chatId, threadSpec);
				const executionCfg = getRuntimeConfigSnapshot() ?? cfg;
				const commandDefinition = findCommandByNativeName(command.name, "telegram");
				const rawText = ctx.match?.trim() ?? "";
				const commandArgs = commandDefinition ? parseCommandArgs(commandDefinition, rawText) : rawText ? { raw: rawText } : void 0;
				const prompt = commandDefinition ? buildCommandTextFromArgs(commandDefinition, commandArgs) : rawText ? `/${command.name} ${rawText}` : `/${command.name}`;
				let cachedTargetSessionKey;
				let cachedNativeCommandRuntime;
				const resolveNativeCommandRuntime = async () => {
					cachedNativeCommandRuntime ??= await loadTelegramNativeCommandRuntime();
					return cachedNativeCommandRuntime;
				};
				const resolveTargetSessionKey = async () => {
					if (cachedTargetSessionKey) return cachedTargetSessionKey;
					cachedTargetSessionKey = resolveCommandTargetSessionKey({
						runtimeCfg,
						route,
						chatId,
						isGroup,
						senderId,
						threadSpec,
						botHasTopicsEnabled: resolveTelegramBotHasTopicsEnabled(ctx.me),
						resolveThreadSessionKeys: (await resolveNativeCommandRuntime()).resolveThreadSessionKeys
					});
					return cachedTargetSessionKey;
				};
				const menuNeedsModelContext = commandDefinition?.argsMenu && !(commandArgs?.raw && !commandArgs.values) && commandDefinition.args?.some((arg) => typeof arg.choices === "function" && commandArgs?.values?.[arg.name] == null);
				const menuModelContext = commandDefinition && menuNeedsModelContext ? resolveTelegramCommandMenuModelContext({
					cfg: runtimeCfg,
					agentId: route.agentId,
					sessionKey: await resolveTargetSessionKey()
				}) : {};
				const menu = commandDefinition ? resolveCommandArgMenu({
					command: commandDefinition,
					args: commandArgs,
					cfg: runtimeCfg,
					...menuModelContext
				}) : null;
				if (menu && commandDefinition) {
					const title = formatTelegramCommandArgMenuTitle({
						command: commandDefinition,
						menu,
						currentThinkingLevel: commandDefinition.key === "think" ? await resolveTelegramThinkMenuCurrentLevel({
							cfg: runtimeCfg,
							agentId: route.agentId,
							...menuModelContext
						}) : void 0
					});
					const rows = [];
					for (let i = 0; i < menu.choices.length; i += 2) {
						const slice = menu.choices.slice(i, i + 2);
						rows.push(slice.map((choice) => {
							const args = { values: { [menu.arg.name]: choice.value } };
							return {
								text: choice.label,
								callback_data: buildTelegramNativeCommandCallbackData(buildCommandTextFromArgs(commandDefinition, args))
							};
						}));
					}
					const replyMarkup = buildInlineKeyboard(rows);
					await withTelegramApiErrorLogging({
						operation: "sendMessage",
						runtime,
						fn: () => bot.api.sendMessage(chatId, title, {
							...replyMarkup ? { reply_markup: replyMarkup } : {},
							...threadParams
						})
					});
					return;
				}
				const nativeCommandRuntime = await resolveNativeCommandRuntime();
				const sessionKey = await resolveTargetSessionKey();
				const { skillFilter, groupSystemPrompt } = resolveTelegramGroupPromptSettings({
					groupConfig,
					topicConfig
				});
				const { sessionKey: commandSessionKey, commandTargetSessionKey } = resolveNativeCommandSessionTargets({
					agentId: route.agentId,
					sessionPrefix: "telegram:slash",
					userId: String(senderId || chatId),
					targetSessionKey: sessionKey
				});
				const deliveryBaseOptions = buildCommandDeliveryBaseOptions({
					cfg: executionCfg,
					chatId,
					accountId: route.accountId,
					sessionKeyForInternalHooks: commandSessionKey,
					policySessionKey: commandTargetSessionKey,
					mirrorIsGroup: isGroup,
					mirrorGroupId: isGroup ? String(chatId) : void 0,
					mediaLocalRoots,
					threadSpec,
					tableMode,
					chunkMode,
					linkPreview: runtimeTelegramCfg.linkPreview
				});
				let topicName;
				if (isForum && resolvedThreadId != null) try {
					topicName = await getTopicName(chatId, resolvedThreadId, resolveTopicNameCacheScope(resolveStorePath(executionCfg.session?.store, { agentId: route.accountId })));
				} catch {}
				const conversationLabel = isGroup ? msg.chat.title ? `${msg.chat.title} id:${chatId}` : `group:${chatId}` : buildSenderName(msg) ?? String(senderId || chatId);
				const ctxPayload = nativeCommandRuntime.finalizeInboundContext({
					Body: prompt,
					BodyForAgent: prompt,
					RawBody: prompt,
					CommandBody: prompt,
					CommandArgs: commandArgs,
					From: isGroup ? buildTelegramGroupFrom(chatId, resolvedThreadId) : `telegram:${chatId}`,
					To: `slash:${senderId || chatId}`,
					ChatType: isGroup ? "group" : "direct",
					ConversationLabel: conversationLabel,
					GroupSubject: isGroup ? msg.chat.title ?? void 0 : void 0,
					GroupSystemPrompt: isGroup || !isGroup && groupConfig ? groupSystemPrompt : void 0,
					SenderName: buildSenderName(msg),
					SenderId: senderId || void 0,
					SenderUsername: senderUsername || void 0,
					Surface: "telegram",
					Provider: "telegram",
					MessageSid: String(msg.message_id),
					Timestamp: msg.date ? msg.date * 1e3 : void 0,
					WasMentioned: true,
					CommandAuthorized: commandAuthorized,
					CommandTurn: {
						kind: "native",
						source: "native",
						authorized: commandAuthorized,
						body: prompt
					},
					CommandSource: "native",
					SessionKey: commandSessionKey,
					AccountId: route.accountId,
					CommandTargetSessionKey: commandTargetSessionKey,
					MessageThreadId: threadSpec.id,
					IsForum: isForum,
					TopicName: isForum && topicName ? topicName : void 0,
					OriginatingChannel: "telegram",
					OriginatingTo: originatingTo
				});
				await nativeCommandRuntime.recordInboundSessionMetaSafe({
					cfg: executionCfg,
					agentId: route.agentId,
					sessionKey: commandTargetSessionKey,
					ctx: ctxPayload,
					onError: (err) => runtime.error?.(danger(`telegram slash: failed updating session meta: ${String(err)}`))
				});
				const disableBlockStreaming = resolveTelegramNativeCommandDisableBlockStreaming(runtimeTelegramCfg);
				const deliveryState = {
					delivered: false,
					skippedNonSilent: 0
				};
				const { createChannelMessageReplyPipeline, deliverReplies } = await loadTelegramNativeCommandDeliveryRuntime();
				const { onModelSelected, ...replyPipeline } = createChannelMessageReplyPipeline({
					cfg: executionCfg,
					agentId: route.agentId,
					channel: "telegram",
					accountId: route.accountId
				});
				await telegramDeps.dispatchReplyWithBufferedBlockDispatcher({
					ctx: ctxPayload,
					cfg: executionCfg,
					dispatcherOptions: {
						...replyPipeline,
						beforeDeliver: async (payload) => payload,
						deliver: async (payload, _info) => {
							if (shouldSuppressLocalTelegramExecApprovalPrompt({
								cfg: executionCfg,
								accountId: route.accountId,
								payload
							})) {
								deliveryState.delivered = true;
								return;
							}
							if ((await deliverReplies({
								replies: [payload.replyToId ? payload : {
									...payload,
									replyToId: String(msg.message_id)
								}],
								...deliveryBaseOptions,
								silent: runtimeTelegramCfg.silentErrorReplies === true && payload.isError === true
							})).delivered) deliveryState.delivered = true;
						},
						onSkip: (_payload, info) => {
							if (info.reason !== "silent") deliveryState.skippedNonSilent += 1;
						},
						onError: (err, info) => {
							runtime.error?.(danger(`telegram slash ${info.kind} reply failed: ${String(err)}`));
						}
					},
					replyOptions: {
						skillFilter,
						disableBlockStreaming,
						onModelSelected
					}
				});
				if (!deliveryState.delivered && deliveryState.skippedNonSilent > 0) await deliverReplies({
					replies: [{ text: EMPTY_RESPONSE_FALLBACK$1 }],
					...deliveryBaseOptions
				});
			});
		}
		for (const pluginCommand of pluginCatalog.commands) bot.command(pluginCommand.command, async (ctx) => {
			const msg = ctx.message;
			if (!msg) return;
			if (shouldSkipUpdate(ctx)) return;
			const chatId = msg.chat.id;
			const runtimeCfg = loadFreshRuntimeConfig();
			const runtimeTelegramCfg = resolveFreshTelegramConfig(runtimeCfg);
			const { threadParams } = await resolveTelegramNativeCommandThreadContext({
				msg,
				bot
			});
			const rawText = ctx.match?.trim() ?? "";
			const commandBody = `/${pluginCommand.command}${rawText ? ` ${rawText}` : ""}`;
			const nativeCommandRuntime = await loadTelegramNativeCommandRuntime();
			const match = nativeCommandRuntime.matchPluginCommand(commandBody);
			if (!match) {
				await withTelegramApiErrorLogging({
					operation: "sendMessage",
					runtime,
					fn: () => bot.api.sendMessage(chatId, "Command not found.", threadParams ?? {})
				});
				return;
			}
			const auth = await resolveTelegramCommandAuth({
				msg,
				bot,
				cfg: runtimeCfg,
				accountId,
				telegramCfg: runtimeTelegramCfg,
				readChannelAllowFromStore: telegramDeps.readChannelAllowFromStore,
				allowFrom,
				groupAllowFrom,
				useAccessGroups,
				resolveGroupPolicy,
				resolveTelegramGroupConfig,
				requireAuth: match.command.requireAuth !== false
			});
			if (!auth) return;
			const { senderId, commandAuthorized, senderIsOwner, isGroup, isForum, resolvedThreadId } = auth;
			const runtimeContext = await resolveCommandRuntimeContext({
				msg,
				runtimeCfg,
				isGroup,
				isForum,
				resolvedThreadId,
				senderId,
				topicAgentId: auth.topicConfig?.agentId
			});
			if (!runtimeContext) return;
			const { threadSpec, route, mediaLocalRoots, tableMode, chunkMode } = runtimeContext;
			const targetSessionKey = resolveCommandTargetSessionKey({
				runtimeCfg,
				route,
				chatId,
				isGroup,
				senderId,
				threadSpec,
				botHasTopicsEnabled: resolveTelegramBotHasTopicsEnabled(ctx.me),
				resolveThreadSessionKeys: nativeCommandRuntime.resolveThreadSessionKeys
			});
			const targetSessionEntry = nativeCommandRuntime.getSessionEntry({
				agentId: route.agentId,
				sessionKey: targetSessionKey
			});
			const deliveryBaseOptions = buildCommandDeliveryBaseOptions({
				cfg: runtimeCfg,
				chatId,
				accountId: route.accountId,
				sessionKeyForInternalHooks: targetSessionKey,
				policySessionKey: targetSessionKey,
				mirrorIsGroup: isGroup,
				mirrorGroupId: isGroup ? String(chatId) : void 0,
				mediaLocalRoots,
				threadSpec,
				tableMode,
				chunkMode,
				linkPreview: runtimeTelegramCfg.linkPreview
			});
			const from = isGroup ? buildTelegramGroupFrom(chatId, threadSpec.id) : `telegram:${chatId}`;
			const to = `telegram:${chatId}`;
			const { deliverReplies, emitTelegramMessageSentHooks } = await loadTelegramNativeCommandDeliveryRuntime();
			let progressMessageId;
			const progressPlaceholder = resolveTelegramProgressPlaceholder(match.command);
			if (progressPlaceholder) try {
				const maybeMessageId = (await withTelegramApiErrorLogging({
					operation: "sendMessage",
					runtime,
					fn: () => bot.api.sendMessage(chatId, progressPlaceholder, buildTelegramThreadParams(threadSpec))
				}))?.message_id;
				if (typeof maybeMessageId === "number") progressMessageId = maybeMessageId;
			} catch {}
			const sessionFileContext = await resolveTelegramCommandSessionFile({
				cfg: runtimeCfg,
				agentId: route.agentId,
				sessionKey: targetSessionKey,
				threadId: threadSpec.id
			});
			const result = normalizeTelegramNativeReplyPayload(await nativeCommandRuntime.executePluginCommand({
				command: match.command,
				args: match.args,
				senderId,
				channel: "telegram",
				isAuthorizedSender: commandAuthorized,
				senderIsOwner,
				agentId: route.agentId,
				sessionKey: targetSessionKey,
				sessionId: sessionFileContext.sessionId,
				sessionFile: sessionFileContext.sessionFile,
				authProfileId: sessionFileContext.authProfileId ?? targetSessionEntry?.authProfileOverride,
				commandBody,
				config: runtimeCfg,
				from,
				to,
				accountId,
				messageThreadId: threadSpec.id
			}));
			if (shouldSuppressLocalTelegramExecApprovalPrompt({
				cfg: runtimeCfg,
				accountId: route.accountId,
				payload: result
			})) {
				await cleanupTelegramProgressPlaceholder({
					bot,
					chatId,
					progressMessageId,
					runtime
				});
				return;
			}
			const deliverableResult = hasRenderableTelegramNativeReplyPayload(result) ? result : { text: EMPTY_RESPONSE_FALLBACK$1 };
			const progressResultText = typeof deliverableResult.text === "string" && deliverableResult.text.trim().length > 0 ? deliverableResult.text : null;
			const telegramResultData = resolveTelegramNativeReplyChannelData(deliverableResult);
			if (progressMessageId != null && telegramDeps.editMessageTelegram && progressResultText && isEditableTelegramProgressResult(deliverableResult)) try {
				await telegramDeps.editMessageTelegram(chatId, progressMessageId, progressResultText, {
					cfg: runtimeCfg,
					accountId: route.accountId,
					textMode: "markdown",
					linkPreview: runtimeTelegramCfg.linkPreview,
					buttons: telegramResultData?.buttons
				});
				recordSentMessage(chatId, progressMessageId, runtimeCfg);
				emitTelegramMessageSentHooks({
					sessionKeyForInternalHooks: targetSessionKey,
					chatId: String(chatId),
					accountId: route.accountId,
					content: progressResultText,
					success: true,
					messageId: progressMessageId,
					isGroup,
					groupId: isGroup ? String(chatId) : void 0
				});
				return;
			} catch {}
			await cleanupTelegramProgressPlaceholder({
				bot,
				chatId,
				progressMessageId,
				runtime
			});
			await deliverReplies({
				replies: [deliverableResult],
				...deliveryBaseOptions,
				silent: runtimeTelegramCfg.silentErrorReplies === true && deliverableResult.isError === true
			});
		});
	} else if (nativeDisabledExplicit) {
		withTelegramApiErrorLogging({
			operation: "setMyCommands",
			runtime,
			fn: () => bot.api.setMyCommands([])
		}).catch(() => {});
		withTelegramApiErrorLogging({
			operation: "setMyCommands(all_group_chats)",
			runtime,
			fn: () => bot.api.setMyCommands([], { scope: { type: "all_group_chats" } })
		}).catch(() => {});
	}
};
//#endregion
//#region extensions/telegram/src/bot-updates.ts
const RECENT_TELEGRAM_UPDATE_TTL_MS = 5 * 6e4;
const RECENT_TELEGRAM_UPDATE_MAX = 2e3;
const resolveTelegramUpdateId = (ctx) => ctx.update?.update_id ?? ctx.update_id;
const buildTelegramUpdateKey = (ctx) => {
	const updateId = resolveTelegramUpdateId(ctx);
	if (typeof updateId === "number") return `update:${updateId}`;
	const callbackId = ctx.callbackQuery?.id;
	if (callbackId) return `callback:${callbackId}`;
	const editedMsg = ctx.editedMessage ?? ctx.editedChannelPost ?? ctx.update?.edited_message ?? ctx.update?.edited_channel_post;
	const editedChatId = editedMsg?.chat?.id;
	const editedMessageId = editedMsg?.message_id;
	if (editedChatId !== void 0 && typeof editedMessageId === "number") return `edited-message:${editedChatId}:${editedMessageId}`;
	const msg = ctx.message ?? ctx.channelPost ?? ctx.update?.message ?? ctx.update?.channel_post ?? ctx.callbackQuery?.message;
	const chatId = msg?.chat?.id;
	const messageId = msg?.message_id;
	if (chatId !== void 0 && typeof messageId === "number") return `message:${chatId}:${messageId}`;
};
const createTelegramUpdateDedupe = () => createDedupeCache({
	ttlMs: RECENT_TELEGRAM_UPDATE_TTL_MS,
	maxSize: RECENT_TELEGRAM_UPDATE_MAX
});
//#endregion
//#region extensions/telegram/src/dm-access.ts
function resolveTelegramSenderIdentity(msg, chatId) {
	const from = msg.from;
	const userId = from?.id != null ? String(from.id) : null;
	return {
		username: from?.username ?? "",
		userId,
		candidateId: userId ?? String(chatId),
		firstName: from?.first_name,
		lastName: from?.last_name
	};
}
async function decideTelegramDmAccess(params) {
	return (await createTelegramIngressResolver({ accountId: params.accountId }).message({
		subject: createTelegramIngressSubject(params.sender.candidateId),
		conversation: {
			kind: "direct",
			id: params.sender.candidateId
		},
		dmPolicy: params.dmPolicy,
		groupPolicy: "disabled",
		allowFrom: telegramAllowEntries(params.effectiveDmAllow)
	})).ingress;
}
async function isTelegramDmAccessAllowed(params) {
	if (params.dmPolicy === "disabled") return false;
	const sender = resolveTelegramSenderIdentity(params.msg, params.chatId);
	return (await decideTelegramDmAccess({
		accountId: params.accountId,
		dmPolicy: params.dmPolicy,
		sender,
		effectiveDmAllow: params.effectiveDmAllow
	})).decision === "allow";
}
async function enforceTelegramDmAccess(params) {
	const { isGroup, dmPolicy, msg, chatId, effectiveDmAllow, accountId, bot, logger, upsertPairingRequest } = params;
	if (isGroup) return true;
	if (dmPolicy === "disabled") return false;
	const sender = resolveTelegramSenderIdentity(msg, chatId);
	const access = await decideTelegramDmAccess({
		accountId,
		dmPolicy,
		sender,
		effectiveDmAllow
	});
	if (access.decision === "allow") return true;
	if (dmPolicy === "open") {
		logVerbose(`Blocked unauthorized telegram sender ${sender.candidateId} (dmPolicy=open)`);
		return false;
	}
	if (access.decision === "pairing") {
		try {
			const telegramUserId = sender.userId ?? sender.candidateId;
			await createChannelPairingChallengeIssuer({
				channel: "telegram",
				upsertPairingRequest: async ({ id, meta }) => await (upsertPairingRequest ?? upsertChannelPairingRequest)({
					channel: "telegram",
					id,
					accountId,
					meta
				})
			})({
				senderId: telegramUserId,
				senderIdLine: `Your Telegram user id: ${telegramUserId}`,
				meta: {
					username: sender.username || void 0,
					firstName: sender.firstName,
					lastName: sender.lastName
				},
				onCreated: () => {
					logger.info({
						chatId: String(chatId),
						senderUserId: sender.userId ?? void 0,
						username: sender.username || void 0,
						firstName: sender.firstName,
						lastName: sender.lastName
					}, "telegram pairing request");
				},
				sendPairingReply: async (text) => {
					const html = renderTelegramHtmlText(text);
					await withTelegramApiErrorLogging({
						operation: "sendMessage",
						fn: () => bot.api.sendMessage(chatId, html, { parse_mode: "HTML" })
					});
				},
				onReplyError: (err) => {
					logVerbose(`telegram pairing reply failed for chat ${chatId}: ${String(err)}`);
				}
			});
		} catch (err) {
			logVerbose(`telegram pairing reply failed for chat ${chatId}: ${String(err)}`);
		}
		return false;
	}
	logVerbose(`Blocked unauthorized telegram sender ${sender.candidateId} (dmPolicy=${dmPolicy})`);
	return false;
}
//#endregion
//#region extensions/telegram/src/forum-service-message.ts
/** Telegram forum-topic service-message fields (Bot API). */
const TELEGRAM_FORUM_SERVICE_FIELDS = [
	"forum_topic_created",
	"forum_topic_edited",
	"forum_topic_closed",
	"forum_topic_reopened",
	"general_forum_topic_hidden",
	"general_forum_topic_unhidden"
];
/**
* Returns `true` when the message is a Telegram forum service message (e.g.
* "Topic created"). These auto-generated messages carry one of the
* `forum_topic_*` / `general_forum_topic_*` fields and should not count as
* regular bot replies for implicit-mention purposes.
*/
function isTelegramForumServiceMessage(msg) {
	if (!msg || typeof msg !== "object") return false;
	const messageRecord = msg;
	return TELEGRAM_FORUM_SERVICE_FIELDS.some((field) => field in messageRecord && messageRecord[field] != null);
}
//#endregion
//#region extensions/telegram/src/group-migration.ts
function resolveAccountGroups(cfg, accountId) {
	if (!accountId) return {};
	const normalized = normalizeAccountId(accountId);
	const accounts = cfg.channels?.telegram?.accounts;
	if (!accounts || typeof accounts !== "object") return {};
	const exact = accounts[normalized];
	if (exact?.groups) return { groups: exact.groups };
	const matchKey = Object.keys(accounts).find((key) => normalizeLowercaseStringOrEmpty(key) === normalizeLowercaseStringOrEmpty(normalized));
	return { groups: matchKey ? accounts[matchKey]?.groups : void 0 };
}
function migrateTelegramGroupsInPlace(groups, oldChatId, newChatId) {
	if (!groups) return {
		migrated: false,
		skippedExisting: false
	};
	if (oldChatId === newChatId) return {
		migrated: false,
		skippedExisting: false
	};
	if (!Object.hasOwn(groups, oldChatId)) return {
		migrated: false,
		skippedExisting: false
	};
	if (Object.hasOwn(groups, newChatId)) return {
		migrated: false,
		skippedExisting: true
	};
	groups[newChatId] = groups[oldChatId];
	delete groups[oldChatId];
	return {
		migrated: true,
		skippedExisting: false
	};
}
function migrateTelegramGroupConfig(params) {
	const scopes = [];
	let migrated = false;
	let skippedExisting = false;
	const migrationTargets = [{
		scope: "account",
		groups: resolveAccountGroups(params.cfg, params.accountId).groups
	}, {
		scope: "global",
		groups: params.cfg.channels?.telegram?.groups
	}];
	for (const target of migrationTargets) {
		const result = migrateTelegramGroupsInPlace(target.groups, params.oldChatId, params.newChatId);
		if (result.migrated) {
			migrated = true;
			scopes.push(target.scope);
		}
		if (result.skippedExisting) skippedExisting = true;
	}
	return {
		migrated,
		skippedExisting,
		scopes
	};
}
//#endregion
//#region extensions/telegram/src/interactive-dispatch.ts
async function dispatchTelegramPluginInteractiveHandler(params) {
	return await dispatchPluginInteractiveHandler({
		channel: "telegram",
		data: params.data,
		dedupeId: params.callbackId,
		onMatched: params.onMatched,
		invoke: ({ registration, namespace, payload }) => {
			const { callbackMessage, ...handlerContext } = params.ctx;
			return registration.handler({
				...handlerContext,
				channel: "telegram",
				callback: {
					data: params.data,
					namespace,
					payload,
					messageId: callbackMessage.messageId,
					chatId: callbackMessage.chatId,
					messageText: callbackMessage.messageText
				},
				respond: params.respond,
				...createInteractiveConversationBindingHelpers({
					registration,
					senderId: handlerContext.senderId,
					conversation: {
						channel: "telegram",
						accountId: handlerContext.accountId,
						conversationId: handlerContext.conversationId,
						parentConversationId: handlerContext.parentConversationId,
						threadId: handlerContext.threadId
					}
				})
			});
		}
	});
}
//#endregion
//#region extensions/telegram/src/bot-handlers.runtime.ts
const registerTelegramHandlers = ({ cfg, accountId, bot, opts, telegramTransport, runtime, mediaMaxBytes, telegramCfg, allowFrom, groupAllowFrom, resolveGroupPolicy, resolveTelegramGroupConfig, shouldSkipUpdate, processMessage, logger, telegramDeps, resolveGroupActivation, resolveGroupRequireMention }) => {
	const mediaRuntimeOptions = resolveTelegramMediaRuntimeOptions({
		cfg,
		accountId,
		token: opts.token,
		transport: telegramTransport
	});
	const DEFAULT_TEXT_FRAGMENT_MAX_GAP_MS = 1500;
	const TELEGRAM_TEXT_FRAGMENT_START_THRESHOLD_CHARS = 4e3;
	const TELEGRAM_TEXT_FRAGMENT_MAX_GAP_MS = typeof opts.testTimings?.textFragmentGapMs === "number" && Number.isFinite(opts.testTimings.textFragmentGapMs) ? Math.max(10, Math.floor(opts.testTimings.textFragmentGapMs)) : DEFAULT_TEXT_FRAGMENT_MAX_GAP_MS;
	const TELEGRAM_TEXT_FRAGMENT_MAX_ID_GAP = 1;
	const TELEGRAM_TEXT_FRAGMENT_MAX_PARTS = 12;
	const TELEGRAM_TEXT_FRAGMENT_MAX_TOTAL_CHARS = 5e4;
	const mediaGroupTimeoutMs = typeof opts.testTimings?.mediaGroupFlushMs === "number" && Number.isFinite(opts.testTimings.mediaGroupFlushMs) ? Math.max(10, Math.floor(opts.testTimings.mediaGroupFlushMs)) : typeof telegramCfg.mediaGroupFlushMs === "number" && Number.isFinite(telegramCfg.mediaGroupFlushMs) ? Math.max(10, Math.floor(telegramCfg.mediaGroupFlushMs)) : 500;
	const mediaGroupBuffer = /* @__PURE__ */ new Map();
	const mediaGroupProcessingByKey = /* @__PURE__ */ new Map();
	const messageCache = createTelegramMessageCache({ scope: resolveTelegramMessageCacheScope(telegramDeps.resolveStorePath(cfg.session?.store)) });
	const messageDispatchReplayGuard = createTelegramMessageDispatchReplayGuard({ onDiskError: (error) => {
		runtime.error?.(danger(`[telegram] message dispatch dedupe store failed: ${String(error)}`));
	} });
	const textFragmentBuffer = /* @__PURE__ */ new Map();
	const textFragmentProcessingByKey = /* @__PURE__ */ new Map();
	const queueBufferedProcessing = async (processingByKey, key, task) => {
		const current = (processingByKey.get(key) ?? Promise.resolve()).then(task).catch(() => void 0);
		processingByKey.set(key, current);
		await current;
		if (processingByKey.get(key) === current) processingByKey.delete(key);
	};
	const debounceMs = resolveInboundDebounceMs({
		cfg,
		channel: "telegram"
	});
	const FORWARD_BURST_DEBOUNCE_MS = 80;
	const normalizePromptContextMinTimestampMs = (timestampMs) => typeof timestampMs === "number" && Number.isFinite(timestampMs) ? timestampMs : void 0;
	const promptContextBoundaryOptions = (timestampMs) => {
		const promptContextMinTimestampMs = normalizePromptContextMinTimestampMs(timestampMs);
		return promptContextMinTimestampMs === void 0 ? {} : { promptContextMinTimestampMs };
	};
	const latestPromptContextMinTimestampMs = (...timestamps) => {
		let latest;
		for (const timestampMs of timestamps) {
			const normalized = normalizePromptContextMinTimestampMs(timestampMs);
			if (normalized === void 0) continue;
			latest = latest === void 0 ? normalized : Math.max(latest, normalized);
		}
		return latest;
	};
	const mergeDispatchDedupeKeys = (...groups) => [...new Set(normalizeStringEntries(groups.flatMap((group) => group ?? [])))];
	const releaseDispatchDedupeKeys = (keys, error) => {
		releaseTelegramMessageDispatchReplay({
			guard: messageDispatchReplayGuard,
			keys,
			error
		});
	};
	const commitDispatchDedupeKeys = async (keys) => {
		await commitTelegramMessageDispatchReplay({
			guard: messageDispatchReplayGuard,
			keys
		});
	};
	const claimMessageDispatchDedupe = async (msg) => {
		const claim = await claimTelegramMessageDispatchReplay({
			guard: messageDispatchReplayGuard,
			accountId,
			msg
		});
		if (claim.kind === "duplicate") {
			logVerbose(`telegram dispatch dedupe: skipped message ${msg.chat.id}:${msg.message_id}`);
			return { process: false };
		}
		return {
			process: true,
			keys: claim.kind === "claimed" ? [claim.key] : []
		};
	};
	const resolveTelegramDebounceLane = (msg) => {
		const forwardMeta = msg;
		return forwardMeta.forward_origin ?? forwardMeta.forward_from ?? forwardMeta.forward_from_chat ?? forwardMeta.forward_sender_name ?? forwardMeta.forward_date ? "forward" : "default";
	};
	const buildSyntheticTextMessage = (params) => ({
		...params.base,
		...params.from ? { from: params.from } : {},
		text: params.text,
		caption: void 0,
		caption_entities: void 0,
		entities: void 0,
		...params.date != null ? { date: params.date } : {}
	});
	const buildSyntheticContext = (ctx, message) => ({
		message,
		me: ctx.me,
		getFile: ctx.getFile.bind(ctx)
	});
	const MULTI_SELECT_PREFIX = "OC_MULTI|";
	const MULTI_SELECT_TOGGLE_PREFIX = `${MULTI_SELECT_PREFIX}toggle|`;
	const SELECT_PREFIX = "OC_SELECT|";
	const SELECTED_PREFIX = "✅ ";
	const parseTelegramManagedSelectCallback = (data) => {
		if (data.startsWith(MULTI_SELECT_TOGGLE_PREFIX)) return {
			type: "multi-toggle",
			value: data.slice(MULTI_SELECT_TOGGLE_PREFIX.length)
		};
		if (data === `${MULTI_SELECT_PREFIX}clear`) return { type: "multi-clear" };
		if (data === `${MULTI_SELECT_PREFIX}submit`) return { type: "multi-submit" };
		if (data.startsWith(SELECT_PREFIX)) return {
			type: "select",
			value: data.slice(10)
		};
	};
	const cloneInlineKeyboardButtons = (message) => {
		const rows = message.reply_markup?.inline_keyboard;
		if (!Array.isArray(rows)) return [];
		return rows.map((row) => Array.isArray(row) ? row.map((button) => {
			const candidate = button;
			if (typeof candidate.text !== "string" || typeof candidate.callback_data !== "string") return null;
			const style = candidate.style === "danger" || candidate.style === "success" || candidate.style === "primary" ? candidate.style : void 0;
			return {
				text: candidate.text,
				callback_data: candidate.callback_data,
				...style ? { style } : {}
			};
		}).filter((button) => button !== null) : []).filter((row) => row.length > 0);
	};
	const stripMultiSelectPrefix = (text) => text.replace(/^✅\s*/, "");
	const isSelectedMultiButton = (button) => /^✅\s*/.test(button.text);
	const isMultiToggleButton = (button) => button.callback_data.startsWith(MULTI_SELECT_TOGGLE_PREFIX);
	const resolveMultiSelectedValues = (buttons) => buttons.flatMap((row) => row.flatMap((button) => {
		if (!isMultiToggleButton(button) || !isSelectedMultiButton(button)) return [];
		return [button.callback_data.slice(MULTI_SELECT_TOGGLE_PREFIX.length)];
	}));
	const updateMultiSelectKeyboard = (message, action, value = "") => cloneInlineKeyboardButtons(message).map((row) => row.map((button) => {
		if (!isMultiToggleButton(button)) return button;
		const buttonValue = button.callback_data.slice(MULTI_SELECT_TOGGLE_PREFIX.length);
		const baseText = stripMultiSelectPrefix(button.text);
		const selected = action === "clear" ? false : buttonValue === value ? !isSelectedMultiButton(button) : isSelectedMultiButton(button);
		return {
			...button,
			text: selected ? `${SELECTED_PREFIX}${baseText}` : baseText
		};
	}));
	const buildCallbackSyntheticTextContext = (params) => {
		const message = buildSyntheticTextMessage({
			base: withResolvedTelegramForumFlag(params.callbackMessage, params.isForum),
			from: params.callback.from,
			text: params.text
		});
		return {
			ctx: buildSyntheticContext(params.ctx, message),
			message
		};
	};
	const inboundDebouncer = createInboundDebouncer({
		debounceMs,
		serializeImmediate: true,
		resolveDebounceMs: (entry) => entry.debounceLane === "forward" ? FORWARD_BURST_DEBOUNCE_MS : debounceMs,
		buildKey: (entry) => entry.debounceKey,
		shouldDebounce: (entry) => {
			const text = getTelegramTextParts(entry.msg).text;
			const hasDebounceableText = shouldDebounceTextInbound({
				text,
				cfg,
				commandOptions: { botUsername: entry.botUsername }
			});
			if (entry.debounceLane === "forward") return hasDebounceableText || entry.allMedia.length > 0;
			if (!hasDebounceableText) return false;
			return entry.allMedia.length === 0;
		},
		onFlush: async (entries) => {
			const last = entries.at(-1);
			if (!last) return;
			if (entries.length === 1) {
				await processMessageWithReplyChain({
					ctx: last.ctx,
					msg: last.msg,
					allMedia: last.allMedia,
					storeAllowFrom: last.storeAllowFrom,
					options: {
						receivedAtMs: last.receivedAtMs,
						ingressBuffer: "inbound-debounce",
						...promptContextBoundaryOptions(last.promptContextMinTimestampMs)
					},
					dispatchDedupeKeys: last.dispatchDedupeKeys
				});
				return;
			}
			const combinedText = entries.map((entry) => getTelegramTextParts(entry.msg).text).filter(Boolean).join("\n");
			const combinedMedia = entries.flatMap((entry) => entry.allMedia);
			if (!combinedText.trim() && combinedMedia.length === 0) return;
			const first = entries[0];
			const promptContextMinTimestampMs = latestPromptContextMinTimestampMs(...entries.map((entry) => entry.promptContextMinTimestampMs));
			const baseCtx = first.ctx;
			const syntheticMessage = buildSyntheticTextMessage({
				base: first.msg,
				text: combinedText,
				date: last.msg.date ?? first.msg.date
			});
			const messageIdOverride = last.msg.message_id ? String(last.msg.message_id) : void 0;
			await processMessageWithReplyChain({
				ctx: buildSyntheticContext(baseCtx, syntheticMessage),
				msg: syntheticMessage,
				allMedia: combinedMedia,
				storeAllowFrom: first.storeAllowFrom,
				options: {
					...messageIdOverride ? { messageIdOverride } : {},
					receivedAtMs: first.receivedAtMs,
					ingressBuffer: "inbound-debounce",
					...promptContextBoundaryOptions(promptContextMinTimestampMs)
				},
				dispatchDedupeKeys: mergeDispatchDedupeKeys(...entries.map((entry) => entry.dispatchDedupeKeys))
			});
		},
		onError: (err, items) => {
			runtime.error?.(danger(`telegram debounce flush failed: ${String(err)}`));
			const chatId = items[0]?.msg.chat.id;
			if (chatId != null) {
				const threadId = items[0]?.msg.message_thread_id;
				bot.api.sendMessage(chatId, "Something went wrong while processing your message. Please try again.", threadId != null ? { message_thread_id: threadId } : void 0).catch((sendErr) => {
					logVerbose(`telegram: error fallback send failed: ${String(sendErr)}`);
				});
			}
		},
		onCancel: (items) => {
			releaseDispatchDedupeKeys(mergeDispatchDedupeKeys(...items.map((item) => item.dispatchDedupeKeys)));
		}
	});
	const resolveTelegramSessionState = (params) => {
		const runtimeCfg = params.runtimeCfg ?? telegramDeps.getRuntimeConfig();
		const resolvedThreadId = params.resolvedThreadId ?? resolveTelegramForumThreadId({
			isForum: params.isForum,
			messageThreadId: params.messageThreadId
		});
		const dmThreadId = !params.isGroup ? params.messageThreadId : void 0;
		const topicThreadId = resolvedThreadId ?? dmThreadId;
		const { topicConfig } = resolveTelegramGroupConfig(params.chatId, topicThreadId);
		const { route } = resolveTelegramConversationRoute({
			cfg: runtimeCfg,
			accountId,
			chatId: params.chatId,
			isGroup: params.isGroup,
			resolvedThreadId,
			replyThreadId: topicThreadId,
			senderId: params.senderId,
			topicAgentId: topicConfig?.agentId
		});
		const baseSessionKey = resolveTelegramConversationBaseSessionKey({
			cfg: runtimeCfg,
			route,
			chatId: params.chatId,
			isGroup: params.isGroup,
			senderId: params.senderId
		});
		const sessionKey = (shouldUseTelegramDmThreadSession({
			dmThreadId,
			botHasTopicsEnabled: params.botHasTopicsEnabled
		}) && dmThreadId != null ? resolveThreadSessionKeys({
			baseSessionKey,
			threadId: `${params.chatId}:${dmThreadId}`
		}) : null)?.sessionKey ?? baseSessionKey;
		const storePath = telegramDeps.resolveStorePath(runtimeCfg.session?.store, { agentId: route.agentId });
		const store = (telegramDeps.loadSessionStore ?? loadSessionStore)(storePath);
		const entry = resolveSessionStoreEntry({
			store,
			sessionKey
		}).existing;
		const storedOverride = resolveStoredModelOverride({
			sessionEntry: entry,
			sessionStore: store,
			sessionKey,
			defaultProvider: resolveDefaultModelForAgent({
				cfg: runtimeCfg,
				agentId: route.agentId
			}).provider
		});
		if (storedOverride) return {
			agentId: route.agentId,
			sessionEntry: entry,
			sessionKey,
			model: storedOverride.provider ? `${storedOverride.provider}/${storedOverride.model}` : storedOverride.model
		};
		const provider = entry?.modelProvider?.trim();
		const model = entry?.model?.trim();
		if (provider && model) return {
			agentId: route.agentId,
			sessionEntry: entry,
			sessionKey,
			model: `${provider}/${model}`
		};
		const modelCfg = runtimeCfg.agents?.defaults?.model;
		return {
			agentId: route.agentId,
			sessionEntry: entry,
			sessionKey,
			model: typeof modelCfg === "string" ? modelCfg : modelCfg?.primary
		};
	};
	const mediaMayNeedDownloadForMentionDetection = (msg) => {
		if (getTelegramTextParts(msg).text.trim()) return false;
		const documentMime = msg.document?.mime_type?.split(";")[0]?.trim().toLowerCase();
		return Boolean(msg.audio ?? msg.voice ?? documentMime?.startsWith("audio/"));
	};
	const shouldSkipMediaDownloadForUnaddressedMentionGroup = async (params) => {
		const { ctx, msg, chatId, isGroup, isForum, resolvedThreadId, dmThreadId, senderId, effectiveGroupAllow, effectiveDmAllow, groupConfig, topicConfig } = params;
		if (!isGroup || mediaMayNeedDownloadForMentionDetection(msg)) return false;
		const runtimeCfg = telegramDeps.getRuntimeConfig();
		const sessionState = resolveTelegramSessionState({
			chatId,
			isGroup,
			isForum,
			resolvedThreadId,
			messageThreadId: resolvedThreadId ?? dmThreadId,
			senderId,
			runtimeCfg
		});
		const activationOverride = resolveGroupActivation({
			chatId,
			messageThreadId: resolvedThreadId,
			sessionKey: sessionState.sessionKey,
			agentId: sessionState.agentId
		});
		if (!firstDefined(topicConfig?.requireMention, activationOverride, groupConfig?.requireMention, resolveGroupRequireMention(chatId))) return false;
		const botUsername = ctx.me?.username?.trim().toLowerCase();
		const mentionRegexes = buildMentionRegexes(runtimeCfg, sessionState.agentId);
		const messageTextParts = getTelegramTextParts(msg);
		const hasAnyMention = messageTextParts.entities.some((ent) => ent.type === "mention");
		const explicitlyMentioned = botUsername ? hasBotMention(msg, botUsername) : false;
		const wasMentioned = matchesMentionWithExplicit({
			text: messageTextParts.text,
			mentionRegexes,
			explicit: {
				hasAnyMention,
				isExplicitlyMentioned: explicitlyMentioned,
				canResolveExplicit: Boolean(botUsername)
			}
		});
		const botId = ctx.me?.id;
		const replyFromId = msg.reply_to_message?.from?.id;
		const replyToBotMessage = botId != null && replyFromId === botId;
		const isReplyToServiceMessage = replyToBotMessage && isTelegramForumServiceMessage(msg.reply_to_message);
		const implicitMentionKinds = implicitMentionKindWhen("reply_to_bot", replyToBotMessage && !isReplyToServiceMessage);
		const canDetectMention = Boolean(botUsername) || mentionRegexes.length > 0;
		const hasControlCommandInMessage = hasControlCommand(messageTextParts.text, runtimeCfg, { botUsername });
		const commandGate = await resolveTelegramCommandIngressAuthorization({
			accountId,
			cfg: runtimeCfg,
			dmPolicy: "pairing",
			isGroup,
			chatId,
			resolvedThreadId,
			senderId,
			effectiveDmAllow,
			effectiveGroupAllow,
			ownerAccess: {
				ownerList: [],
				senderIsOwner: false
			},
			eventKind: "message",
			allowTextCommands: true,
			hasControlCommand: hasControlCommandInMessage,
			modeWhenAccessGroupsOff: "allow",
			includeDmAllowForGroupCommands: false
		});
		if (resolveInboundMentionDecision({
			facts: {
				canDetectMention,
				wasMentioned,
				hasAnyMention,
				implicitMentionKinds
			},
			policy: {
				isGroup,
				requireMention: true,
				allowTextCommands: true,
				hasControlCommand: hasControlCommandInMessage,
				commandAuthorized: commandGate.authorized
			}
		}).shouldSkip) {
			logger.info({
				chatId,
				reason: "no-mention"
			}, "skipping group media before download");
			return true;
		}
		return false;
	};
	const processMediaGroup = async (entry) => {
		try {
			entry.messages.sort((a, b) => a.msg.message_id - b.msg.message_id);
			const primaryEntry = entry.messages.find((m) => m.msg.caption || m.msg.text) ?? entry.messages[0];
			if (!primaryEntry) {
				releaseDispatchDedupeKeys(entry.dispatchDedupeKeys);
				return;
			}
			if (await shouldSkipMediaDownloadForUnaddressedMentionGroup({
				ctx: primaryEntry.ctx,
				msg: primaryEntry.msg,
				chatId: primaryEntry.msg.chat.id,
				isGroup: entry.isGroup,
				isForum: entry.isForum,
				resolvedThreadId: entry.resolvedThreadId,
				dmThreadId: entry.dmThreadId,
				senderId: entry.senderId,
				effectiveGroupAllow: entry.effectiveGroupAllow,
				effectiveDmAllow: entry.effectiveDmAllow,
				groupConfig: entry.groupConfig,
				topicConfig: entry.topicConfig
			})) {
				releaseDispatchDedupeKeys(entry.dispatchDedupeKeys);
				return;
			}
			const allMedia = [];
			let skippedCount = 0;
			for (const { ctx } of entry.messages) {
				let media;
				try {
					media = await resolveMedia({
						ctx,
						maxBytes: mediaMaxBytes,
						...mediaRuntimeOptions
					});
				} catch (mediaErr) {
					if (!isRecoverableMediaGroupError(mediaErr)) throw mediaErr;
					runtime.log?.(warn(`media group: skipping photo that failed to fetch: ${String(mediaErr)}`));
					skippedCount++;
					continue;
				}
				if (media) allMedia.push({
					path: media.path,
					contentType: media.contentType,
					stickerMetadata: media.stickerMetadata
				});
				else skippedCount++;
			}
			if (skippedCount > 0) {
				const total = entry.messages.length;
				const wasOrWere = skippedCount === 1 ? "was" : "were";
				await withTelegramApiErrorLogging({
					operation: "sendMessage",
					runtime,
					fn: () => bot.api.sendMessage(primaryEntry.msg.chat.id, `⚠️ Received ${allMedia.length} of ${total} images — ${skippedCount} could not be fetched and ${wasOrWere} skipped.`, { reply_parameters: {
						message_id: primaryEntry.msg.message_id,
						allow_sending_without_reply: true
					} })
				}).catch(() => {});
			}
			await processMessageWithReplyChain({
				ctx: primaryEntry.ctx,
				msg: primaryEntry.msg,
				allMedia,
				storeAllowFrom: entry.storeAllowFrom,
				options: promptContextBoundaryOptions(entry.promptContextMinTimestampMs),
				dispatchDedupeKeys: entry.dispatchDedupeKeys
			});
		} catch (err) {
			releaseDispatchDedupeKeys(entry.dispatchDedupeKeys, err);
			runtime.error?.(danger(`media group handler failed: ${String(err)}`));
		}
	};
	const flushTextFragments = async (entry) => {
		try {
			entry.messages.sort((a, b) => a.msg.message_id - b.msg.message_id);
			const first = entry.messages[0];
			const last = entry.messages.at(-1);
			if (!first || !last) {
				releaseDispatchDedupeKeys(entry.dispatchDedupeKeys);
				return;
			}
			const combinedText = entry.messages.map((m) => m.msg.text ?? "").join("");
			if (!combinedText.trim()) {
				releaseDispatchDedupeKeys(entry.dispatchDedupeKeys);
				return;
			}
			const syntheticMessage = buildSyntheticTextMessage({
				base: first.msg,
				text: combinedText,
				date: last.msg.date ?? first.msg.date
			});
			const storeAllowFrom = await loadStoreAllowFrom(first.msg);
			const baseCtx = first.ctx;
			await processMessageWithReplyChain({
				ctx: buildSyntheticContext(baseCtx, syntheticMessage),
				msg: syntheticMessage,
				allMedia: [],
				storeAllowFrom,
				options: {
					messageIdOverride: String(last.msg.message_id),
					receivedAtMs: first.receivedAtMs,
					ingressBuffer: "text-fragment",
					...promptContextBoundaryOptions(entry.promptContextMinTimestampMs)
				},
				dispatchDedupeKeys: entry.dispatchDedupeKeys
			});
		} catch (err) {
			releaseDispatchDedupeKeys(entry.dispatchDedupeKeys, err);
			runtime.error?.(danger(`text fragment handler failed: ${String(err)}`));
		}
	};
	const queueTextFragmentFlush = async (entry) => {
		await queueBufferedProcessing(textFragmentProcessingByKey, entry.key, async () => {
			await flushTextFragments(entry);
		});
	};
	const runTextFragmentFlush = async (entry) => {
		textFragmentBuffer.delete(entry.key);
		await queueTextFragmentFlush(entry);
	};
	const scheduleTextFragmentFlush = (entry) => {
		clearTimeout(entry.timer);
		entry.timer = setTimeout(() => {
			runTextFragmentFlush(entry);
		}, TELEGRAM_TEXT_FRAGMENT_MAX_GAP_MS);
	};
	const loadStoreAllowFrom = async (msg) => {
		const isGroup = msg.chat.type === "group" || msg.chat.type === "supergroup";
		const { groupConfig, topicConfig } = resolveTelegramGroupConfig(msg.chat.id, msg.message_thread_id);
		const groupAllowOverride = firstDefined(topicConfig?.allowFrom, groupConfig?.allowFrom);
		const effectiveDmPolicy = resolveTelegramEffectiveDmPolicy({
			isGroup,
			groupConfig,
			dmPolicy: telegramCfg.dmPolicy
		});
		return loadTelegramPairingStoreIfNeeded({
			cfg,
			allowFrom,
			groupAllowOverride,
			accountId,
			senderId: msg.from?.id != null ? String(msg.from.id) : void 0,
			isGroup,
			effectiveDmPolicy,
			readChannelAllowFromStore: telegramDeps.readChannelAllowFromStore
		});
	};
	const recordMessageForReplyChain = (msg, threadId) => messageCache.record({
		accountId,
		chatId: msg.chat.id,
		msg,
		...threadId != null ? { threadId } : {}
	});
	const buildReplyChainForMessage = (msg) => buildTelegramReplyChain({
		cache: messageCache,
		accountId,
		chatId: msg.chat.id,
		msg
	});
	const toReplyChainEntry = (node, media) => {
		const { sourceMessage: _sourceMessage, ...entry } = node;
		return {
			...entry,
			...media?.path ? { mediaPath: media.path } : {},
			...media?.contentType ? { mediaType: media.contentType } : {}
		};
	};
	const toPromptContextMessage = (node, flags) => ({
		message_id: node.messageId,
		thread_id: node.threadId,
		sender: node.sender,
		sender_id: node.senderId,
		sender_username: node.senderUsername,
		timestamp_ms: node.timestamp,
		body: node.body,
		media_type: node.mediaType,
		media_ref: node.mediaRef,
		reply_to_id: node.replyToId,
		is_reply_target: flags?.replyTarget === true ? true : void 0
	});
	const buildPromptContextForMessage = async (msg, replyChainNodes, options) => {
		const messageId = typeof msg.message_id === "number" ? String(msg.message_id) : void 0;
		const currentNode = await messageCache.get({
			accountId,
			chatId: msg.chat.id,
			messageId
		});
		const threadId = currentNode?.threadId ? Number(currentNode.threadId) : void 0;
		const conversationContext = await buildTelegramConversationContext({
			cache: messageCache,
			messageId,
			accountId,
			chatId: msg.chat.id,
			...Number.isFinite(threadId) ? { threadId } : {},
			replyChainNodes,
			recentLimit: 10,
			replyTargetWindowSize: 2,
			...options?.promptContextMinTimestampMs !== void 0 ? { minTimestampMs: options.promptContextMinTimestampMs } : {}
		});
		return conversationContext.length > 0 ? [{
			label: "Conversation context",
			source: "telegram",
			type: "chat_window",
			payload: {
				order: "chronological",
				relation: "selected_for_current_message",
				messages: conversationContext.map((entry) => toPromptContextMessage(entry.node, { replyTarget: entry.isReplyTarget }))
			}
		}] : [];
	};
	const resolveReplyMediaForChain = async (ctx, chain) => {
		const replyMedia = [];
		const replyChain = [];
		for (const node of chain) {
			let mediaRef;
			const replyFileId = resolveInboundMediaFileId(node.sourceMessage);
			if (replyFileId && hasInboundMedia(node.sourceMessage)) try {
				const media = await resolveMedia({
					ctx: {
						message: node.sourceMessage,
						me: ctx.me,
						getFile: async () => await bot.api.getFile(replyFileId)
					},
					maxBytes: mediaMaxBytes,
					...mediaRuntimeOptions
				});
				mediaRef = media ? {
					path: media.path,
					...media.contentType ? { contentType: media.contentType } : {},
					...media.stickerMetadata ? { stickerMetadata: media.stickerMetadata } : {}
				} : void 0;
			} catch (err) {
				logger.warn({
					chatId: ctx.message.chat.id,
					error: String(err)
				}, "reply media fetch failed");
			}
			if (mediaRef) replyMedia.push(mediaRef);
			replyChain.push(toReplyChainEntry(node, mediaRef));
		}
		return {
			replyMedia,
			replyChain
		};
	};
	const processMessageWithReplyChain = async (params) => {
		let dispatchDedupeCommitted = false;
		try {
			const replyChainNodes = await buildReplyChainForMessage(params.msg);
			const { replyMedia, replyChain } = await resolveReplyMediaForChain(params.ctx, replyChainNodes);
			const promptContext = await buildPromptContextForMessage(params.msg, replyChainNodes, params.options);
			if (!await processMessage(params.ctx, params.allMedia, params.storeAllowFrom, params.options, replyMedia, replyChain, promptContext, { onDispatchStart: async () => {
				await commitDispatchDedupeKeys(params.dispatchDedupeKeys ?? []);
				dispatchDedupeCommitted = true;
			} }) && !dispatchDedupeCommitted) releaseDispatchDedupeKeys(params.dispatchDedupeKeys ?? []);
		} catch (err) {
			if (!dispatchDedupeCommitted) releaseDispatchDedupeKeys(params.dispatchDedupeKeys ?? [], err);
			throw err;
		}
	};
	const shouldSkipGroupMessage = (params) => {
		const { isGroup, chatId, chatTitle, resolvedThreadId, senderId, senderUsername, effectiveGroupAllow, hasGroupAllowOverride, groupConfig, topicConfig } = params;
		const baseAccess = evaluateTelegramGroupBaseAccess({
			isGroup,
			groupConfig,
			topicConfig,
			hasGroupAllowOverride,
			effectiveGroupAllow,
			senderId,
			senderUsername,
			enforceAllowOverride: true,
			requireSenderForAllowOverride: true
		});
		if (!baseAccess.allowed) {
			if (baseAccess.reason === "group-disabled") {
				logVerbose(`Blocked telegram group ${chatId} (group disabled)`);
				return true;
			}
			if (baseAccess.reason === "topic-disabled") {
				logVerbose(`Blocked telegram topic ${chatId} (${resolvedThreadId ?? "unknown"}) (topic disabled)`);
				return true;
			}
			logVerbose(`Blocked telegram group sender ${senderId || "unknown"} (group allowFrom override)`);
			return true;
		}
		if (!isGroup) return false;
		const policyAccess = evaluateTelegramGroupPolicyAccess({
			isGroup,
			chatId,
			cfg,
			telegramCfg,
			topicConfig,
			groupConfig,
			effectiveGroupAllow,
			senderId,
			senderUsername,
			resolveGroupPolicy,
			enforcePolicy: true,
			useTopicAndGroupOverrides: true,
			enforceAllowlistAuthorization: true,
			allowEmptyAllowlistEntries: false,
			requireSenderForAllowlistAuthorization: true,
			checkChatAllowlist: true
		});
		if (!policyAccess.allowed) {
			if (policyAccess.reason === "group-policy-disabled") {
				logVerbose("Blocked telegram group message (groupPolicy: disabled)");
				return true;
			}
			if (policyAccess.reason === "group-policy-allowlist-no-sender") {
				logVerbose("Blocked telegram group message (no sender ID, groupPolicy: allowlist)");
				return true;
			}
			if (policyAccess.reason === "group-policy-allowlist-empty") {
				logVerbose("Blocked telegram group message (groupPolicy: allowlist, no group allowlist entries)");
				return true;
			}
			if (policyAccess.reason === "group-policy-allowlist-unauthorized") {
				logVerbose(`Blocked telegram group message from ${senderId} (groupPolicy: allowlist)`);
				return true;
			}
			logger.info({
				chatId,
				title: chatTitle,
				reason: "not-allowed"
			}, "skipping group message");
			return true;
		}
		return false;
	};
	const getChat = bot.api.getChat.bind(bot.api);
	const TELEGRAM_EVENT_AUTH_RULES = {
		reaction: {
			enforceDirectAuthorization: true,
			enforceGroupAllowlistAuthorization: false,
			deniedDmReason: "reaction unauthorized by dm policy/allowlist",
			deniedGroupReason: "reaction unauthorized by group allowlist"
		},
		"callback-scope": {
			enforceDirectAuthorization: false,
			enforceGroupAllowlistAuthorization: false,
			deniedDmReason: "callback unauthorized by inlineButtonsScope",
			deniedGroupReason: "callback unauthorized by inlineButtonsScope"
		},
		"callback-allowlist": {
			enforceDirectAuthorization: true,
			enforceGroupAllowlistAuthorization: false,
			deniedDmReason: "callback unauthorized by inlineButtonsScope allowlist",
			deniedGroupReason: "callback unauthorized by inlineButtonsScope allowlist"
		}
	};
	class TelegramRetryableCallbackError extends Error {
		constructor(cause) {
			super(String(cause));
			this.cause = cause;
			this.name = "TelegramRetryableCallbackError";
		}
	}
	const TELEGRAM_PERMANENT_CALLBACK_EDIT_ERROR_RE = /400:\s*Bad Request:\s*message to edit not found|400:\s*Bad Request:\s*there is no text in the message to edit|MESSAGE_ID_INVALID|400:\s*Bad Request:\s*message can't be edited/i;
	const isPermanentTelegramCallbackEditError = (err) => TELEGRAM_PERMANENT_CALLBACK_EDIT_ERROR_RE.test(String(err));
	const resolveTelegramEventAuthorizationContext = async (params) => {
		const groupAllowContext = params.groupAllowContext ?? await resolveTelegramGroupAllowFromContext({
			cfg,
			chatId: params.chatId,
			accountId,
			dmPolicy: telegramCfg.dmPolicy,
			allowFrom,
			senderId: params.senderId,
			isGroup: params.isGroup,
			isForum: params.isForum,
			messageThreadId: params.messageThreadId,
			groupAllowFrom,
			readChannelAllowFromStore: telegramDeps.readChannelAllowFromStore,
			resolveTelegramGroupConfig
		});
		return {
			dmPolicy: resolveTelegramEffectiveDmPolicy({
				isGroup: params.isGroup,
				groupConfig: groupAllowContext.groupConfig,
				dmPolicy: telegramCfg.dmPolicy
			}),
			...groupAllowContext
		};
	};
	const authorizeTelegramEventSender = async (params) => {
		const { chatId, chatTitle, isGroup, senderId, senderUsername, mode, context } = params;
		const { dmPolicy, resolvedThreadId, storeAllowFrom, groupConfig, topicConfig, groupAllowOverride, effectiveGroupAllow, hasGroupAllowOverride } = context;
		const { enforceDirectAuthorization, enforceGroupAllowlistAuthorization, deniedDmReason, deniedGroupReason } = TELEGRAM_EVENT_AUTH_RULES[mode];
		if (shouldSkipGroupMessage({
			isGroup,
			chatId,
			chatTitle,
			resolvedThreadId,
			senderId,
			senderUsername,
			effectiveGroupAllow,
			hasGroupAllowOverride,
			groupConfig,
			topicConfig
		})) return false;
		if (!isGroup && enforceDirectAuthorization) {
			const eventAccess = await resolveTelegramEventIngressAuthorization({
				accountId,
				dmPolicy,
				isGroup,
				chatId,
				resolvedThreadId,
				senderId,
				effectiveDmAllow: normalizeDmAllowFromWithStore({
					allowFrom: await expandTelegramAllowFromWithAccessGroups({
						cfg,
						allowFrom: groupAllowOverride ?? allowFrom,
						accountId,
						senderId
					}),
					storeAllowFrom,
					dmPolicy
				}),
				effectiveGroupAllow,
				enforceGroupAuthorization: false,
				eventKind: mode === "reaction" ? "reaction" : "button"
			});
			if (eventAccess.decision !== "allow") {
				if (eventAccess.reasonCode === "dm_policy_disabled") {
					logVerbose(`Blocked telegram direct event from ${senderId || "unknown"} (${deniedDmReason})`);
					return false;
				}
				logVerbose(`Blocked telegram direct sender ${senderId || "unknown"} (${deniedDmReason})`);
				return false;
			}
		}
		if (isGroup && enforceGroupAllowlistAuthorization) {
			if ((await resolveTelegramEventIngressAuthorization({
				accountId,
				dmPolicy,
				isGroup,
				chatId,
				resolvedThreadId,
				senderId,
				effectiveDmAllow: normalizeDmAllowFromWithStore({
					allowFrom: [],
					dmPolicy
				}),
				effectiveGroupAllow,
				enforceGroupAuthorization: true,
				eventKind: mode === "reaction" ? "reaction" : "button"
			})).decision !== "allow") {
				logVerbose(`Blocked telegram group sender ${senderId || "unknown"} (${deniedGroupReason})`);
				return false;
			}
		}
		return true;
	};
	const isTelegramModelCallbackAuthorized = async (params) => {
		const { chatId, isGroup, senderId, senderUsername, context, cfg: cfgLocal } = params;
		const dmAllowFrom = context.groupAllowOverride ?? allowFrom;
		if (isTelegramCommandsAllowFromConfigured(cfgLocal)) return resolveTelegramCommandAuthorization({
			cfg: cfgLocal,
			accountId,
			chatId,
			isGroup,
			resolvedThreadId: context.resolvedThreadId,
			senderId,
			senderUsername
		}).isAuthorizedSender;
		const dmAllow = normalizeDmAllowFromWithStore({
			allowFrom: await expandTelegramAllowFromWithAccessGroups({
				cfg: cfgLocal,
				allowFrom: dmAllowFrom,
				accountId,
				senderId
			}),
			storeAllowFrom: isGroup ? [] : context.storeAllowFrom,
			dmPolicy: context.dmPolicy
		});
		return (await resolveTelegramCommandIngressAuthorization({
			accountId,
			cfg: cfgLocal,
			dmPolicy: context.dmPolicy,
			isGroup,
			chatId,
			resolvedThreadId: context.resolvedThreadId,
			senderId,
			effectiveDmAllow: dmAllow,
			effectiveGroupAllow: context.effectiveGroupAllow,
			ownerAccess: {
				ownerList: [],
				senderIsOwner: false
			},
			eventKind: "button"
		})).authorized;
	};
	bot.on("message_reaction", async (ctx) => {
		try {
			const reaction = ctx.messageReaction;
			if (!reaction) return;
			if (shouldSkipUpdate(ctx)) return;
			const chatId = reaction.chat.id;
			const messageId = reaction.message_id;
			const user = reaction.user;
			const senderId = user?.id != null ? String(user.id) : "";
			const senderUsername = user?.username ?? "";
			const isGroup = reaction.chat.type === "group" || reaction.chat.type === "supergroup";
			const isForum = reaction.chat.is_forum === true;
			const reactionMode = telegramCfg.reactionNotifications ?? "own";
			if (reactionMode === "off") return;
			if (user?.is_bot) return;
			if (reactionMode === "own" && !telegramDeps.wasSentByBot(chatId, messageId, cfg)) {
				logVerbose(`telegram: skipped reaction on msg ${messageId} in chat ${chatId} (own mode, not sent by bot)`);
				return;
			}
			const eventAuthContext = await resolveTelegramEventAuthorizationContext({
				chatId,
				isGroup,
				isForum,
				senderId
			});
			if (!await authorizeTelegramEventSender({
				chatId,
				chatTitle: reaction.chat.title,
				isGroup,
				senderId,
				senderUsername,
				mode: "reaction",
				context: eventAuthContext
			})) return;
			if (!isGroup) {
				if (eventAuthContext.groupConfig?.requireTopic === true) {
					logVerbose(`Blocked telegram reaction in DM ${chatId}: requireTopic=true but topic unknown for reactions`);
					return;
				}
			}
			const oldEmojis = new Set(reaction.old_reaction.filter((r) => r.type === "emoji").map((r) => r.emoji));
			const addedReactions = reaction.new_reaction.filter((r) => r.type === "emoji").filter((r) => !oldEmojis.has(r.emoji));
			if (addedReactions.length === 0) return;
			const senderName = user ? [user.first_name, user.last_name].filter(Boolean).join(" ").trim() || user.username : void 0;
			const senderUsernameLabel = user?.username ? `@${user.username}` : void 0;
			let senderLabel = senderName;
			if (senderName && senderUsernameLabel) senderLabel = `${senderName} (${senderUsernameLabel})`;
			else if (!senderName && senderUsernameLabel) senderLabel = senderUsernameLabel;
			if (!senderLabel && user?.id) senderLabel = `id:${user.id}`;
			senderLabel = senderLabel || "unknown";
			const resolvedThreadId = isForum ? resolveTelegramForumThreadId({
				isForum,
				messageThreadId: void 0
			}) : void 0;
			const peerId = isGroup ? buildTelegramGroupPeerId(chatId, resolvedThreadId) : String(chatId);
			const parentPeer = buildTelegramParentPeer({
				isGroup,
				resolvedThreadId,
				chatId
			});
			const sessionKey = resolveAgentRoute({
				cfg: telegramDeps.getRuntimeConfig(),
				channel: "telegram",
				accountId,
				peer: {
					kind: isGroup ? "group" : "direct",
					id: peerId
				},
				parentPeer
			}).sessionKey;
			for (const r of addedReactions) {
				const emoji = r.emoji;
				const text = `Telegram reaction added: ${emoji} by ${senderLabel} on msg ${messageId}`;
				telegramDeps.enqueueSystemEvent(text, {
					sessionKey,
					contextKey: `telegram:reaction:add:${chatId}:${messageId}:${user?.id ?? "anon"}:${emoji}`
				});
				logVerbose(`telegram: reaction event enqueued: ${text}`);
			}
		} catch (err) {
			runtime.error?.(danger(`telegram reaction handler failed: ${String(err)}`));
			throw err;
		}
	});
	const processInboundMessage = async (params) => {
		const { ctx, msg, chatId, isGroup, isForum, resolvedThreadId, dmThreadId, dmPolicy, storeAllowFrom, senderId, effectiveGroupAllow, effectiveDmAllow, groupConfig, topicConfig, sendOversizeWarning, oversizeLogMessage, promptContextMinTimestampMs, dispatchDedupeKeys } = params;
		const messageText = getTelegramTextParts(msg).text;
		const botUsername = ctx.me?.username;
		const isAbortControlMessage = isAbortRequestText(messageText, { botUsername });
		let abortControlAuthorized;
		const isAuthorizedAbortControlMessage = () => {
			if (!isAbortControlMessage || !senderId) return Promise.resolve(false);
			abortControlAuthorized ??= resolveTelegramCommandIngressAuthorization({
				accountId,
				cfg,
				dmPolicy,
				isGroup,
				chatId,
				resolvedThreadId,
				senderId,
				effectiveDmAllow,
				effectiveGroupAllow,
				ownerAccess: {
					ownerList: [],
					senderIsOwner: false
				},
				eventKind: "message",
				allowTextCommands: true,
				hasControlCommand: true,
				modeWhenAccessGroupsOff: "allow",
				includeDmAllowForGroupCommands: false
			}).then((gate) => gate.authorized);
			return abortControlAuthorized;
		};
		const text = typeof msg.text === "string" ? msg.text : void 0;
		const isCommandLike = (text ?? "").trim().startsWith("/");
		if (text && !isCommandLike && !isAbortControlMessage) {
			const nowMs = Date.now();
			const senderIdValue = msg.from?.id != null ? String(msg.from.id) : "unknown";
			const key = `text:${chatId}:${resolvedThreadId ?? dmThreadId ?? "main"}:${senderIdValue}`;
			const existing = textFragmentBuffer.get(key);
			if (existing) {
				const last = existing.messages.at(-1);
				const lastMsgId = last?.msg.message_id;
				const lastReceivedAtMs = last?.receivedAtMs ?? nowMs;
				const idGap = typeof lastMsgId === "number" ? msg.message_id - lastMsgId : Infinity;
				const timeGapMs = nowMs - lastReceivedAtMs;
				if (idGap > 0 && idGap <= TELEGRAM_TEXT_FRAGMENT_MAX_ID_GAP && timeGapMs >= 0 && timeGapMs <= TELEGRAM_TEXT_FRAGMENT_MAX_GAP_MS) {
					const nextTotalChars = existing.messages.reduce((sum, m) => sum + (m.msg.text?.length ?? 0), 0) + text.length;
					if (existing.messages.length + 1 <= TELEGRAM_TEXT_FRAGMENT_MAX_PARTS && nextTotalChars <= TELEGRAM_TEXT_FRAGMENT_MAX_TOTAL_CHARS) {
						existing.messages.push({
							msg,
							ctx,
							receivedAtMs: nowMs
						});
						existing.promptContextMinTimestampMs = latestPromptContextMinTimestampMs(existing.promptContextMinTimestampMs, promptContextMinTimestampMs);
						existing.dispatchDedupeKeys = mergeDispatchDedupeKeys(existing.dispatchDedupeKeys, dispatchDedupeKeys);
						scheduleTextFragmentFlush(existing);
						return;
					}
				}
				clearTimeout(existing.timer);
				textFragmentBuffer.delete(key);
				await queueTextFragmentFlush(existing);
			}
			if (text.length >= TELEGRAM_TEXT_FRAGMENT_START_THRESHOLD_CHARS) {
				const entry = {
					key,
					messages: [{
						msg,
						ctx,
						receivedAtMs: nowMs
					}],
					dispatchDedupeKeys,
					...promptContextBoundaryOptions(promptContextMinTimestampMs),
					timer: setTimeout(() => {}, TELEGRAM_TEXT_FRAGMENT_MAX_GAP_MS)
				};
				textFragmentBuffer.set(key, entry);
				scheduleTextFragmentFlush(entry);
				return;
			}
		} else if (text && isAbortControlMessage && await isAuthorizedAbortControlMessage()) {
			const senderIdLocal = msg.from?.id != null ? String(msg.from.id) : "unknown";
			const key = `text:${chatId}:${resolvedThreadId ?? dmThreadId ?? "main"}:${senderIdLocal}`;
			const existing = textFragmentBuffer.get(key);
			if (existing) {
				clearTimeout(existing.timer);
				textFragmentBuffer.delete(key);
				releaseDispatchDedupeKeys(existing.dispatchDedupeKeys);
			}
		}
		const mediaGroupId = msg.media_group_id;
		if (mediaGroupId) {
			const mediaGroupKey = `media:${chatId}:${resolvedThreadId ?? dmThreadId ?? "main"}:${mediaGroupId}`;
			const existing = mediaGroupBuffer.get(mediaGroupKey);
			if (existing) {
				clearTimeout(existing.timer);
				existing.messages.push({
					msg,
					ctx
				});
				existing.promptContextMinTimestampMs = latestPromptContextMinTimestampMs(existing.promptContextMinTimestampMs, promptContextMinTimestampMs);
				existing.dispatchDedupeKeys = mergeDispatchDedupeKeys(existing.dispatchDedupeKeys, dispatchDedupeKeys);
				existing.timer = setTimeout(() => {
					mediaGroupBuffer.delete(mediaGroupKey);
					queueBufferedProcessing(mediaGroupProcessingByKey, mediaGroupKey, async () => {
						await processMediaGroup(existing);
					});
				}, mediaGroupTimeoutMs);
			} else {
				const entry = {
					messages: [{
						msg,
						ctx
					}],
					storeAllowFrom,
					isGroup,
					isForum,
					resolvedThreadId,
					dmThreadId,
					senderId,
					effectiveGroupAllow,
					effectiveDmAllow,
					groupConfig,
					topicConfig,
					dispatchDedupeKeys,
					...promptContextBoundaryOptions(promptContextMinTimestampMs),
					timer: setTimeout(() => {
						mediaGroupBuffer.delete(mediaGroupKey);
						queueBufferedProcessing(mediaGroupProcessingByKey, mediaGroupKey, async () => {
							await processMediaGroup(entry);
						});
					}, mediaGroupTimeoutMs)
				};
				mediaGroupBuffer.set(mediaGroupKey, entry);
			}
			return;
		}
		if (await shouldSkipMediaDownloadForUnaddressedMentionGroup({
			ctx,
			msg,
			chatId,
			isGroup,
			isForum,
			resolvedThreadId,
			dmThreadId,
			senderId,
			effectiveGroupAllow,
			effectiveDmAllow,
			groupConfig,
			topicConfig
		})) {
			releaseDispatchDedupeKeys(dispatchDedupeKeys);
			return;
		}
		let media;
		try {
			media = await resolveMedia({
				ctx,
				maxBytes: mediaMaxBytes,
				...mediaRuntimeOptions
			});
		} catch (mediaErr) {
			if (isMediaSizeLimitError(mediaErr)) {
				if (sendOversizeWarning) {
					const limitMb = Math.round(mediaMaxBytes / (1024 * 1024));
					await withTelegramApiErrorLogging({
						operation: "sendMessage",
						runtime,
						fn: () => bot.api.sendMessage(chatId, `⚠️ File too large. Maximum size is ${limitMb}MB.`, { reply_parameters: {
							message_id: msg.message_id,
							allow_sending_without_reply: true
						} })
					}).catch(() => {});
				}
				logger.warn({
					chatId,
					error: String(mediaErr)
				}, oversizeLogMessage);
				releaseDispatchDedupeKeys(dispatchDedupeKeys);
				return;
			}
			logger.warn({
				chatId,
				error: String(mediaErr)
			}, "media fetch failed");
			await withTelegramApiErrorLogging({
				operation: "sendMessage",
				runtime,
				fn: () => bot.api.sendMessage(chatId, "⚠️ Failed to download media. Please try again.", { reply_parameters: {
					message_id: msg.message_id,
					allow_sending_without_reply: true
				} })
			}).catch(() => {});
			releaseDispatchDedupeKeys(dispatchDedupeKeys);
			return;
		}
		const hasText = Boolean(getTelegramTextParts(msg).text.trim());
		if (msg.sticker && !media && !hasText) {
			logVerbose("telegram: skipping sticker-only message (unsupported sticker type)");
			releaseDispatchDedupeKeys(dispatchDedupeKeys);
			return;
		}
		const allMedia = media ? [{
			path: media.path,
			contentType: media.contentType,
			stickerMetadata: media.stickerMetadata
		}] : [];
		const conversationKey = buildTelegramInboundDebounceConversationKey({
			chatId,
			threadId: resolvedThreadId ?? dmThreadId
		});
		const debounceLane = resolveTelegramDebounceLane(msg);
		const debounceKey = senderId ? buildTelegramInboundDebounceKey({
			accountId,
			conversationKey,
			senderId,
			debounceLane
		}) : null;
		if (senderId && await isAuthorizedAbortControlMessage()) for (const lane of ["default", "forward"]) inboundDebouncer.cancelKey(buildTelegramInboundDebounceKey({
			accountId,
			conversationKey,
			senderId,
			debounceLane: lane
		}));
		await inboundDebouncer.enqueue({
			ctx,
			msg,
			allMedia,
			storeAllowFrom,
			receivedAtMs: Date.now(),
			debounceKey: isAbortControlMessage ? null : debounceKey,
			debounceLane,
			botUsername,
			...promptContextBoundaryOptions(promptContextMinTimestampMs),
			dispatchDedupeKeys
		});
	};
	bot.on("callback_query", async (ctx) => {
		const callback = ctx.callbackQuery;
		if (!callback) return;
		if (shouldSkipUpdate(ctx)) return;
		await withTelegramApiErrorLogging({
			operation: "answerCallbackQuery",
			runtime,
			fn: () => bot.api.answerCallbackQuery(callback.id)
		}).catch(() => {});
		try {
			const data = (callback.data ?? "").trim();
			const callbackMessage = callback.message;
			if (!data || !callbackMessage) return;
			const callbackBusinessParams = callbackMessage.business_connection_id !== void 0 ? { business_connection_id: callbackMessage.business_connection_id } : void 0;
			const withCallbackBusinessParams = (params) => callbackBusinessParams ? {
				...callbackBusinessParams,
				...params
			} : params;
			const editCallbackMessage = async (text, params) => {
				return await bot.api.editMessageText(callbackMessage.chat.id, callbackMessage.message_id, text, params ? withCallbackBusinessParams(params) : callbackBusinessParams);
			};
			const clearCallbackButtons = async () => {
				return await bot.api.editMessageReplyMarkup(callbackMessage.chat.id, callbackMessage.message_id, withCallbackBusinessParams({ reply_markup: { inline_keyboard: [] } }));
			};
			const editCallbackButtons = async (buttons) => {
				const replyMarkup = { reply_markup: buildInlineKeyboard(buttons) ?? { inline_keyboard: [] } };
				return await bot.api.editMessageReplyMarkup(callbackMessage.chat.id, callbackMessage.message_id, withCallbackBusinessParams(replyMarkup));
			};
			const deleteCallbackMessage = async () => {
				return await bot.api.deleteMessage(callbackMessage.chat.id, callbackMessage.message_id);
			};
			const replyToCallbackChat = async (text, params) => {
				const threadParams = buildTelegramThreadParams(resolveTelegramThreadSpec({
					isGroup,
					isForum,
					messageThreadId: callbackMessage.message_thread_id
				}));
				const topicParams = {
					...callbackBusinessParams,
					...threadParams,
					...callbackMessage.direct_messages_topic?.topic_id != null ? { direct_messages_topic_id: callbackMessage.direct_messages_topic.topic_id } : {}
				};
				const replyParams = Object.keys(topicParams).length > 0 || params ? {
					...topicParams,
					...params
				} : params;
				return await bot.api.sendMessage(callbackMessage.chat.id, text, replyParams);
			};
			const chatId = callbackMessage.chat.id;
			const isGroup = callbackMessage.chat.type === "group" || callbackMessage.chat.type === "supergroup";
			const nativeCallbackCommand = parseTelegramNativeCommandCallbackData(data);
			const opaqueCallbackData = parseTelegramOpaqueCallbackData(data);
			const callbackCommandText = nativeCallbackCommand ?? (opaqueCallbackData ? "" : data);
			const pluginCallbackData = opaqueCallbackData ?? data;
			const approvalCallback = parseExecApprovalCommandText(nativeCallbackCommand ?? (opaqueCallbackData ? "" : data));
			const isApprovalCallback = approvalCallback !== null;
			const inlineButtonsScope = resolveTelegramInlineButtonsScope({
				cfg,
				accountId
			});
			const execApprovalButtonsEnabled = isApprovalCallback && shouldEnableTelegramExecApprovalButtons({
				cfg,
				accountId,
				to: String(chatId)
			});
			if (!execApprovalButtonsEnabled) {
				if (inlineButtonsScope === "off") return;
				if (inlineButtonsScope === "dm" && isGroup) return;
				if (inlineButtonsScope === "group" && !isGroup) return;
			}
			const messageThreadId = callbackMessage.message_thread_id;
			const isForum = await resolveTelegramForumFlag({
				chatId,
				chatType: callbackMessage.chat.type,
				isGroup,
				isForum: callbackMessage.chat.is_forum,
				isTopicMessage: callbackMessage.is_topic_message,
				getChat
			});
			const senderId = callback.from?.id ? String(callback.from.id) : "";
			const senderUsername = callback.from?.username ?? "";
			const eventAuthContext = await resolveTelegramEventAuthorizationContext({
				chatId,
				isGroup,
				isForum,
				senderId,
				messageThreadId
			});
			const { resolvedThreadId, dmThreadId, storeAllowFrom, groupConfig } = eventAuthContext;
			const requireTopic = groupConfig?.requireTopic;
			if (!isGroup && requireTopic === true && dmThreadId == null) {
				logVerbose(`Blocked telegram callback in DM ${chatId}: requireTopic=true but no topic present`);
				return;
			}
			const authorizationMode = !isGroup || !execApprovalButtonsEnabled && inlineButtonsScope === "allowlist" ? "callback-allowlist" : "callback-scope";
			if (!await authorizeTelegramEventSender({
				chatId,
				chatTitle: callbackMessage.chat.title,
				isGroup,
				senderId,
				senderUsername,
				mode: authorizationMode,
				context: eventAuthContext
			})) return;
			const callbackThreadId = resolvedThreadId ?? dmThreadId;
			const callbackConversationId = callbackThreadId != null ? `${chatId}:topic:${callbackThreadId}` : String(chatId);
			const pluginBindingApproval = parsePluginBindingApprovalCustomId(data);
			if (pluginBindingApproval) {
				let resolved;
				try {
					resolved = await resolvePluginConversationBindingApproval({
						approvalId: pluginBindingApproval.approvalId,
						decision: pluginBindingApproval.decision,
						senderId: senderId || void 0
					});
				} catch (err) {
					throw new TelegramRetryableCallbackError(err);
				}
				await clearCallbackButtons();
				await replyToCallbackChat(buildPluginBindingResolvedText(resolved));
				return;
			}
			const runtimeCfg = telegramDeps.getRuntimeConfig();
			if ((await dispatchTelegramPluginInteractiveHandler({
				data: pluginCallbackData,
				callbackId: callback.id,
				ctx: {
					accountId,
					callbackId: callback.id,
					conversationId: callbackConversationId,
					parentConversationId: callbackThreadId != null ? String(chatId) : void 0,
					senderId: senderId || void 0,
					senderUsername: senderUsername || void 0,
					threadId: callbackThreadId,
					isGroup,
					isForum,
					auth: { isAuthorizedSender: await isTelegramModelCallbackAuthorized({
						chatId,
						isGroup,
						senderId,
						senderUsername,
						context: eventAuthContext,
						cfg: runtimeCfg
					}) },
					callbackMessage: {
						messageId: callbackMessage.message_id,
						chatId: String(chatId),
						messageText: callbackMessage.text ?? callbackMessage.caption
					}
				},
				respond: {
					reply: async ({ text, buttons }) => {
						await replyToCallbackChat(text, buttons ? { reply_markup: buildInlineKeyboard(buttons) } : void 0);
					},
					editMessage: async ({ text, buttons }) => {
						await editCallbackMessage(text, buttons ? { reply_markup: buildInlineKeyboard(buttons) } : void 0);
					},
					editButtons: async ({ buttons }) => {
						await editCallbackButtons(buttons);
					},
					clearButtons: async () => {
						await clearCallbackButtons();
					},
					deleteMessage: async () => {
						await deleteCallbackMessage();
					}
				}
			})).handled) return;
			const managedSelectCallback = parseTelegramManagedSelectCallback(data);
			if (managedSelectCallback) {
				if (managedSelectCallback.type === "multi-toggle" || managedSelectCallback.type === "multi-clear") {
					const buttons = updateMultiSelectKeyboard(callbackMessage, managedSelectCallback.type === "multi-clear" ? "clear" : "toggle", managedSelectCallback.type === "multi-toggle" ? managedSelectCallback.value : "");
					if (buttons.length > 0) try {
						await editCallbackButtons(buttons);
					} catch (editErr) {
						if (!String(editErr).includes("message is not modified")) throw new TelegramRetryableCallbackError(editErr);
					}
					return;
				}
				if (managedSelectCallback.type === "multi-submit") {
					const selected = resolveMultiSelectedValues(cloneInlineKeyboardButtons(callbackMessage));
					const synthetic = buildCallbackSyntheticTextContext({
						ctx,
						callbackMessage,
						callback,
						text: `Multi-select submitted: ${selected.length > 0 ? selected.join(", ") : "none"}`,
						isForum
					});
					await processMessageWithReplyChain({
						ctx: synthetic.ctx,
						msg: synthetic.message,
						allMedia: [],
						storeAllowFrom,
						options: {
							forceWasMentioned: true,
							messageIdOverride: callback.id
						}
					});
					return;
				}
				try {
					await clearCallbackButtons();
				} catch (editErr) {
					const errStr = String(editErr);
					if (!errStr.includes("message is not modified") && !errStr.includes("there is no text in the message to edit")) throw new TelegramRetryableCallbackError(editErr);
				}
				const synthetic = buildCallbackSyntheticTextContext({
					ctx,
					callbackMessage,
					callback,
					text: `Single-select submitted: ${managedSelectCallback.value}`,
					isForum
				});
				await processMessageWithReplyChain({
					ctx: synthetic.ctx,
					msg: synthetic.message,
					allMedia: [],
					storeAllowFrom,
					options: {
						forceWasMentioned: true,
						messageIdOverride: callback.id
					}
				});
				return;
			}
			if (approvalCallback) {
				const isPluginApproval = approvalCallback.approvalId.startsWith("plugin:");
				const pluginApprovalAuthorizedSender = isTelegramExecApprovalApprover({
					cfg: runtimeCfg,
					accountId,
					senderId
				});
				const execApprovalAuthorizedSender = isTelegramExecApprovalAuthorizedSender({
					cfg: runtimeCfg,
					accountId,
					senderId
				});
				if (!(isPluginApproval ? pluginApprovalAuthorizedSender : execApprovalAuthorizedSender || pluginApprovalAuthorizedSender)) {
					logVerbose(`Blocked telegram approval callback from ${senderId || "unknown"} (not authorized)`);
					return;
				}
				try {
					await (telegramDeps.resolveExecApproval ?? resolveTelegramExecApproval)({
						cfg: runtimeCfg,
						approvalId: approvalCallback.approvalId,
						decision: approvalCallback.decision,
						senderId,
						allowPluginFallback: pluginApprovalAuthorizedSender
					});
				} catch (resolveErr) {
					const errStr = String(resolveErr);
					logVerbose(`telegram: failed to resolve approval callback ${approvalCallback.approvalId}: ${errStr}`);
					if (isApprovalNotFoundError(resolveErr)) {
						if (isPluginApproval || pluginApprovalAuthorizedSender) try {
							await clearCallbackButtons();
						} catch (editErr) {
							logVerbose(`telegram: failed to clear expired approval callback buttons: ${String(editErr)}`);
						}
						return;
					}
					throw new TelegramRetryableCallbackError(resolveErr);
				}
				try {
					await clearCallbackButtons();
				} catch (editErr) {
					const errStr = String(editErr);
					if (errStr.includes("message is not modified") || errStr.includes("there is no text in the message to edit")) return;
					logVerbose(`telegram: failed to clear approval callback buttons: ${errStr}`);
				}
				return;
			}
			if (opaqueCallbackData) return;
			const paginationMatch = data.match(/^commands_page_(\d+|noop)(?::(.+))?$/);
			if (paginationMatch) {
				const pageValue = paginationMatch[1];
				if (pageValue === "noop") return;
				const page = parseStrictPositiveInteger(pageValue);
				if (page === void 0) return;
				const agentId = paginationMatch[2]?.trim() || resolveDefaultAgentId(runtimeCfg);
				let result;
				try {
					result = buildCommandsMessagePaginated(runtimeCfg, telegramDeps.listSkillCommandsForAgents({
						cfg: runtimeCfg,
						agentIds: [agentId]
					}), {
						page,
						forcePaginatedList: true,
						surface: "telegram"
					});
				} catch (err) {
					throw new TelegramRetryableCallbackError(err);
				}
				const keyboard = result.totalPages > 1 ? buildInlineKeyboard(buildCommandsPaginationKeyboard(result.currentPage, result.totalPages, agentId)) : void 0;
				try {
					await editCallbackMessage(result.text, keyboard ? { reply_markup: keyboard } : void 0);
				} catch (editErr) {
					if (!String(editErr).includes("message is not modified")) throw new TelegramRetryableCallbackError(editErr);
				}
				return;
			}
			const modelCallback = parseModelCallbackData(data);
			if (modelCallback) {
				if (!await isTelegramModelCallbackAuthorized({
					chatId,
					isGroup,
					senderId,
					senderUsername,
					context: eventAuthContext,
					cfg: runtimeCfg
				})) {
					logVerbose(`Blocked telegram model callback from ${senderId || "unknown"} (not authorized for /models)`);
					return;
				}
				let sessionState;
				let modelData;
				try {
					sessionState = resolveTelegramSessionState({
						chatId,
						isGroup,
						isForum,
						messageThreadId,
						resolvedThreadId,
						botHasTopicsEnabled: resolveTelegramBotHasTopicsEnabled(ctx.me),
						senderId
					});
					modelData = await telegramDeps.buildModelsProviderData(runtimeCfg, sessionState.agentId);
				} catch (err) {
					throw new TelegramRetryableCallbackError(err);
				}
				const { byProvider, providers, modelNames } = modelData;
				const editMessageWithButtons = async (text, buttons, extra) => {
					const keyboard = buildInlineKeyboard(buttons);
					const editParams = keyboard ? {
						reply_markup: keyboard,
						...extra
					} : extra;
					try {
						await editCallbackMessage(text, editParams);
					} catch (editErr) {
						const errStr = String(editErr);
						if (errStr.includes("no text in the message")) {
							try {
								await deleteCallbackMessage();
							} catch {}
							await replyToCallbackChat(text, keyboard ? {
								reply_markup: keyboard,
								...extra
							} : extra);
						} else if (!errStr.includes("message is not modified")) throw editErr;
					}
				};
				if (modelCallback.type === "providers" || modelCallback.type === "back") {
					if (providers.length === 0) {
						try {
							await editMessageWithButtons("No providers available.", []);
						} catch (err) {
							throw new TelegramRetryableCallbackError(err);
						}
						return;
					}
					const buttons = buildTelegramModelsMenuButtons({ providers: providers.map((p) => ({
						id: p,
						count: byProvider.get(p)?.size ?? 0
					})) });
					try {
						await editMessageWithButtons("Select a provider:", buttons);
					} catch (err) {
						throw new TelegramRetryableCallbackError(err);
					}
					return;
				}
				if (modelCallback.type === "list") {
					const { provider, page } = modelCallback;
					const modelSet = byProvider.get(provider);
					if (!modelSet || modelSet.size === 0) {
						const buttons = buildTelegramModelsMenuButtons({ providers: providers.map((p) => ({
							id: p,
							count: byProvider.get(p)?.size ?? 0
						})) });
						try {
							await editMessageWithButtons(`Unknown provider: ${provider}\n\nSelect a provider:`, buttons);
						} catch (err) {
							throw new TelegramRetryableCallbackError(err);
						}
						return;
					}
					const models = [...modelSet].toSorted((left, right) => left.localeCompare(right));
					const pageSize = getModelsPageSize();
					const totalPages = calculateTotalPages(models.length, pageSize);
					const safePage = Math.max(1, Math.min(page, totalPages));
					const currentModel = sessionState.model;
					const buttons = buildModelsKeyboard({
						provider,
						models,
						currentModel,
						currentPage: safePage,
						totalPages,
						pageSize,
						modelNames
					});
					const text = formatModelsAvailableHeader({
						provider,
						total: models.length,
						cfg,
						agentDir: resolveAgentDir(cfg, sessionState.agentId),
						sessionEntry: sessionState.sessionEntry
					});
					try {
						await editMessageWithButtons(text, buttons);
					} catch (err) {
						throw new TelegramRetryableCallbackError(err);
					}
					return;
				}
				if (modelCallback.type === "select") {
					const selection = resolveModelSelection({
						callback: modelCallback,
						providers,
						byProvider
					});
					if (selection.kind !== "resolved") {
						const buttons = buildTelegramModelsMenuButtons({ providers: providers.map((p) => ({
							id: p,
							count: byProvider.get(p)?.size ?? 0
						})) });
						try {
							await editMessageWithButtons(`Could not resolve model "${selection.model}".\n\nSelect a provider:`, buttons);
						} catch (err) {
							throw new TelegramRetryableCallbackError(err);
						}
						return;
					}
					if (!byProvider.get(selection.provider)?.has(selection.model)) {
						try {
							await editMessageWithButtons(`❌ Model "${selection.provider}/${selection.model}" is not allowed.`, []);
						} catch (err) {
							throw new TelegramRetryableCallbackError(err);
						}
						return;
					}
					try {
						const storePath = telegramDeps.resolveStorePath(runtimeCfg.session?.store, { agentId: sessionState.agentId });
						const resolvedDefault = resolveDefaultModelForAgent({
							cfg: runtimeCfg,
							agentId: sessionState.agentId
						});
						const isDefaultSelection = selection.provider === resolvedDefault.provider && selection.model === resolvedDefault.model;
						try {
							await updateSessionStore(storePath, (store) => {
								const sessionKey = sessionState.sessionKey;
								const entry = store[sessionKey] ?? {};
								store[sessionKey] = entry;
								applyModelOverrideToSessionEntry({
									entry,
									selection: {
										provider: selection.provider,
										model: selection.model,
										isDefault: isDefaultSelection
									}
								});
							});
						} catch (err) {
							throw new TelegramRetryableCallbackError(err);
						}
						const escapeHtml = (text) => text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
						await editMessageWithButtons(`✅ Model ${isDefaultSelection ? "reset to default" : `changed to <b>${escapeHtml(selection.provider)}/${escapeHtml(selection.model)}</b>`}\n\n${isDefaultSelection ? "Session selection cleared. Runtime unchanged. New replies use the agent's configured default." : `Session-only model selection. Runtime unchanged. Use /model ${escapeHtml(selection.provider)}/${escapeHtml(selection.model)} --runtime &lt;runtime&gt; to switch harnesses. The agent default in openclaw.json is unchanged; /reset or a new session may return to that default.`}`, [], { parse_mode: "HTML" });
					} catch (err) {
						if (err instanceof TelegramRetryableCallbackError) throw err;
						await editMessageWithButtons(`❌ Failed to change model: ${String(err)}`, []);
					}
					return;
				}
				return;
			}
			const syntheticMessage = buildSyntheticTextMessage({
				base: withResolvedTelegramForumFlag(callbackMessage, isForum),
				from: callback.from,
				text: callbackCommandText
			});
			await processMessageWithReplyChain({
				ctx: buildSyntheticContext(ctx, syntheticMessage),
				msg: syntheticMessage,
				allMedia: [],
				storeAllowFrom,
				options: {
					...nativeCallbackCommand ? { commandSource: "native" } : {},
					forceWasMentioned: true,
					messageIdOverride: callback.id
				}
			});
		} catch (err) {
			if (err instanceof TelegramRetryableCallbackError) {
				if (isPermanentTelegramCallbackEditError(err.cause)) {
					logVerbose(`telegram: swallowing permanent callback edit error: ${String(err.cause)}`);
					return;
				}
				runtime.error?.(danger(`callback handler failed: ${String(err)}`));
				throw err.cause;
			}
			runtime.error?.(danger(`callback handler failed: ${String(err)}`));
		}
	});
	bot.on("message:migrate_to_chat_id", async (ctx) => {
		try {
			const msg = ctx.message;
			if (!msg?.migrate_to_chat_id) return;
			if (shouldSkipUpdate(ctx)) return;
			const oldChatId = String(msg.chat.id);
			const newChatId = String(msg.migrate_to_chat_id);
			const chatTitle = msg.chat.title ?? "Unknown";
			runtime.log?.(warn(`[telegram] Group migrated: "${chatTitle}" ${oldChatId} → ${newChatId}`));
			if (!resolveChannelConfigWrites({
				cfg,
				channelId: "telegram",
				accountId
			})) {
				runtime.log?.(warn("[telegram] Config writes disabled; skipping group config migration."));
				return;
			}
			const migration = migrateTelegramGroupConfig({
				cfg: telegramDeps.getRuntimeConfig(),
				accountId,
				oldChatId,
				newChatId
			});
			if (migration.migrated) {
				runtime.log?.(warn(`[telegram] Migrating group config from ${oldChatId} to ${newChatId}`));
				migrateTelegramGroupConfig({
					cfg,
					accountId,
					oldChatId,
					newChatId
				});
				await mutateConfigFile({
					afterWrite: { mode: "auto" },
					mutate: (draft) => {
						migrateTelegramGroupConfig({
							cfg: draft,
							accountId,
							oldChatId,
							newChatId
						});
					}
				});
				runtime.log?.(warn(`[telegram] Group config migrated and saved successfully`));
			} else if (migration.skippedExisting) runtime.log?.(warn(`[telegram] Group config already exists for ${newChatId}; leaving ${oldChatId} unchanged`));
			else runtime.log?.(warn(`[telegram] No config found for old group ID ${oldChatId}, migration logged only`));
		} catch (err) {
			runtime.error?.(danger(`[telegram] Group migration handler failed: ${String(err)}`));
			throw err;
		}
	});
	const normalizeChannelPostMessage = (post) => {
		const chatId = post.chat.id;
		const syntheticFrom = post.sender_chat ? {
			id: post.sender_chat.id,
			is_bot: true,
			first_name: post.sender_chat.title || "Channel",
			username: post.sender_chat.username
		} : {
			id: chatId,
			is_bot: true,
			first_name: post.chat.title || "Channel",
			username: post.chat.username
		};
		return {
			...post,
			from: post.from ?? syntheticFrom,
			chat: {
				...post.chat,
				type: "supergroup"
			}
		};
	};
	const authorizeInboundMessage = async (params) => {
		const context = await resolveTelegramEventAuthorizationContext({
			chatId: params.chatId,
			isGroup: params.isGroup,
			isForum: params.isForum,
			senderId: params.senderId,
			messageThreadId: params.messageThreadId
		});
		const { dmPolicy, resolvedThreadId, dmThreadId, storeAllowFrom, groupConfig, topicConfig, groupAllowOverride, effectiveGroupAllow, hasGroupAllowOverride } = context;
		const effectiveDmAllow = normalizeDmAllowFromWithStore({
			allowFrom: await expandTelegramAllowFromWithAccessGroups({
				cfg,
				allowFrom: groupAllowOverride ?? allowFrom,
				accountId,
				senderId: params.senderId
			}),
			storeAllowFrom,
			dmPolicy
		});
		if (params.requireConfiguredGroup && (!groupConfig || groupConfig.enabled === false)) {
			logVerbose(`Blocked telegram channel ${params.chatId} (channel disabled)`);
			return { allowed: false };
		}
		if (shouldSkipGroupMessage({
			isGroup: params.isGroup,
			chatId: params.chatId,
			chatTitle: params.msg.chat.title,
			resolvedThreadId,
			senderId: params.senderId,
			senderUsername: params.senderUsername,
			effectiveGroupAllow,
			hasGroupAllowOverride,
			groupConfig,
			topicConfig
		})) return { allowed: false };
		if (!params.isGroup) {
			if ((groupConfig && "requireTopic" in groupConfig ? groupConfig.requireTopic : void 0) === true && dmThreadId == null) {
				logVerbose(`Blocked telegram DM ${params.chatId}: requireTopic=true but no topic present`);
				return { allowed: false };
			}
			if (!(params.dmAccess === "challenge" ? await enforceTelegramDmAccess({
				isGroup: params.isGroup,
				dmPolicy,
				msg: params.msg,
				chatId: params.chatId,
				effectiveDmAllow,
				accountId,
				bot,
				logger,
				upsertPairingRequest: telegramDeps.upsertChannelPairingRequest
			}) : await isTelegramDmAccessAllowed({
				dmPolicy,
				msg: params.msg,
				chatId: params.chatId,
				effectiveDmAllow,
				accountId
			}))) return { allowed: false };
		}
		return {
			allowed: true,
			context,
			effectiveDmAllow
		};
	};
	const recordEditedMessageForReplyChain = async (params) => {
		if (shouldSkipUpdate(params.ctxForDedupe)) return;
		const msg = params.msg;
		const isGroup = msg.chat.type === "group" || msg.chat.type === "supergroup";
		const isForum = await resolveTelegramForumFlag({
			chatId: msg.chat.id,
			chatType: msg.chat.type,
			isGroup,
			isForum: msg.chat.is_forum,
			isTopicMessage: msg.is_topic_message,
			getChat
		});
		const normalizedMsg = withResolvedTelegramForumFlag(msg, isForum);
		const gate = await authorizeInboundMessage({
			msg: normalizedMsg,
			chatId: normalizedMsg.chat.id,
			isGroup,
			isForum,
			messageThreadId: normalizedMsg.message_thread_id,
			senderId: normalizedMsg.from?.id != null ? String(normalizedMsg.from.id) : "",
			senderUsername: normalizedMsg.from?.username ?? "",
			requireConfiguredGroup: params.requireConfiguredGroup,
			dmAccess: "silent"
		});
		if (!gate.allowed) return;
		const { resolvedThreadId, dmThreadId } = gate.context;
		await recordMessageForReplyChain(normalizedMsg, resolvedThreadId ?? dmThreadId);
	};
	const handleInboundMessageLike = async (event) => {
		let dispatchDedupeKeys = [];
		try {
			if (shouldSkipUpdate(event.ctxForDedupe)) return;
			const gate = await authorizeInboundMessage({
				msg: event.msg,
				chatId: event.chatId,
				isGroup: event.isGroup,
				isForum: event.isForum,
				messageThreadId: event.messageThreadId,
				senderId: event.senderId,
				senderUsername: event.senderUsername,
				requireConfiguredGroup: event.requireConfiguredGroup,
				dmAccess: "challenge"
			});
			if (!gate.allowed) return;
			const { effectiveDmAllow } = gate;
			const { dmPolicy, resolvedThreadId, dmThreadId, storeAllowFrom, groupConfig, topicConfig, effectiveGroupAllow } = gate.context;
			const promptContextMinTimestampMs = normalizePromptContextMinTimestampMs(resolveTelegramSessionState({
				chatId: event.chatId,
				isGroup: event.isGroup,
				isForum: event.isForum,
				messageThreadId: event.messageThreadId,
				resolvedThreadId,
				botHasTopicsEnabled: resolveTelegramBotHasTopicsEnabled(event.ctx.me),
				senderId: event.senderId,
				runtimeCfg: cfg
			}).sessionEntry?.sessionStartedAt);
			const dispatchDedupe = await claimMessageDispatchDedupe(event.msg);
			if (!dispatchDedupe.process) return;
			dispatchDedupeKeys = dispatchDedupe.keys;
			await recordMessageForReplyChain(event.msg, resolvedThreadId ?? dmThreadId);
			await processInboundMessage({
				ctx: event.ctx,
				msg: event.msg,
				chatId: event.chatId,
				isGroup: event.isGroup,
				isForum: event.isForum,
				resolvedThreadId,
				dmThreadId,
				dmPolicy,
				storeAllowFrom,
				senderId: event.senderId,
				effectiveGroupAllow,
				effectiveDmAllow,
				groupConfig: event.isGroup ? groupConfig : void 0,
				topicConfig,
				sendOversizeWarning: event.sendOversizeWarning,
				oversizeLogMessage: event.oversizeLogMessage,
				dispatchDedupeKeys,
				...promptContextBoundaryOptions(promptContextMinTimestampMs)
			});
		} catch (err) {
			releaseDispatchDedupeKeys(dispatchDedupeKeys, err);
			runtime.error?.(danger(`${event.errorMessage}: ${String(err)}`));
			if (err instanceof TelegramPairingStoreReadError) await withTelegramApiErrorLogging({
				operation: "sendMessage",
				runtime,
				fn: () => bot.api.sendMessage(event.chatId, "⚠️ Couldn't process this message, please try again in a moment.", { reply_parameters: {
					message_id: event.msg.message_id,
					allow_sending_without_reply: true
				} })
			}).catch(() => {});
		}
	};
	bot.on("message", async (ctx) => {
		const msg = ctx.message;
		if (!msg) return;
		const isGroup = msg.chat.type === "group" || msg.chat.type === "supergroup";
		const isForum = await resolveTelegramForumFlag({
			chatId: msg.chat.id,
			chatType: msg.chat.type,
			isGroup,
			isForum: msg.chat.is_forum,
			isTopicMessage: msg.is_topic_message,
			getChat
		});
		const normalizedMsg = withResolvedTelegramForumFlag(msg, isForum);
		if (normalizedMsg.from?.id != null && normalizedMsg.from.id === ctx.me?.id) return;
		await handleInboundMessageLike({
			ctxForDedupe: ctx,
			ctx: buildSyntheticContext(ctx, normalizedMsg),
			msg: normalizedMsg,
			chatId: normalizedMsg.chat.id,
			isGroup,
			isForum,
			messageThreadId: normalizedMsg.message_thread_id,
			senderId: normalizedMsg.from?.id != null ? String(normalizedMsg.from.id) : "",
			senderUsername: normalizedMsg.from?.username ?? "",
			requireConfiguredGroup: false,
			sendOversizeWarning: true,
			oversizeLogMessage: "media exceeds size limit",
			errorMessage: "handler failed"
		});
	});
	bot.on("edited_message", async (ctx) => {
		const msg = ctx.editedMessage;
		if (!msg) return;
		await recordEditedMessageForReplyChain({
			ctxForDedupe: ctx,
			msg,
			requireConfiguredGroup: false
		});
	});
	bot.on("channel_post", async (ctx) => {
		const post = ctx.channelPost;
		if (!post) return;
		const chatId = post.chat.id;
		const syntheticMsg = normalizeChannelPostMessage(post);
		await handleInboundMessageLike({
			ctxForDedupe: ctx,
			ctx: buildSyntheticContext(ctx, syntheticMsg),
			msg: syntheticMsg,
			chatId,
			isGroup: true,
			isForum: false,
			senderId: post.sender_chat?.id != null ? String(post.sender_chat.id) : post.from?.id != null ? String(post.from.id) : "",
			senderUsername: post.sender_chat?.username ?? post.from?.username ?? "",
			requireConfiguredGroup: true,
			sendOversizeWarning: false,
			oversizeLogMessage: "channel post media exceeds size limit",
			errorMessage: "channel_post handler failed"
		});
	});
	bot.on("edited_channel_post", async (ctx) => {
		const post = ctx.editedChannelPost;
		if (!post) return;
		await recordEditedMessageForReplyChain({
			ctxForDedupe: ctx,
			msg: normalizeChannelPostMessage(post),
			requireConfiguredGroup: true
		});
	});
};
//#endregion
//#region extensions/telegram/src/bot-message-context.body.ts
let stickerVisionRuntimePromise;
let mediaUnderstandingRuntimePromise;
function loadStickerVisionRuntime() {
	stickerVisionRuntimePromise ??= import("./sticker-vision.runtime.js");
	return stickerVisionRuntimePromise;
}
function loadMediaUnderstandingRuntime() {
	mediaUnderstandingRuntimePromise ??= import("./media-understanding.runtime.js");
	return mediaUnderstandingRuntimePromise;
}
function formatAudioTranscriptForAgent(transcript) {
	return `[Audio transcript (machine-generated, untrusted)]: ${JSON.stringify(transcript)}`;
}
function resolveSavedMediaKind(contentType) {
	const normalized = contentType?.split(";")[0]?.trim().toLowerCase();
	if (normalized?.startsWith("audio/")) return "audio";
	if (normalized?.startsWith("image/")) return "image";
	if (normalized?.startsWith("video/")) return "video";
	return "document";
}
function formatSavedMediaPlaceholder(allMedia) {
	if (allMedia.length === 0) return;
	const kinds = allMedia.map((media) => resolveSavedMediaKind(media.contentType));
	const firstKind = kinds[0] ?? "document";
	const kind = kinds.every((candidate) => candidate === firstKind) ? firstKind : "document";
	if (allMedia.length === 1) return `<media:${kind}>`;
	if (kind === "image") return `<media:image> (${allMedia.length} images)`;
	if (kind === "video") return `<media:video> (${allMedia.length} videos)`;
	if (kind === "audio") return `<media:audio> (${allMedia.length} audio attachments)`;
	return `<media:document> (${allMedia.length} attachments)`;
}
async function resolveStickerVisionSupport$1(params) {
	try {
		const { resolveStickerVisionSupportRuntime } = await loadStickerVisionRuntime();
		return await resolveStickerVisionSupportRuntime(params);
	} catch {
		return false;
	}
}
async function resolveTelegramInboundBody(params) {
	const { cfg, primaryCtx, msg, allMedia, isGroup, chatId, accountId, senderId, senderUsername, sessionKey, resolvedThreadId, replyThreadId, originatingTo: providedOriginatingTo, routeAgentId, effectiveGroupAllow, effectiveDmAllow, groupConfig, topicConfig, providerMentionPatterns, requireMention, options, groupHistories, historyLimit, logger } = params;
	const botUsername = normalizeOptionalLowercaseString(primaryCtx.me?.username);
	const mentionRegexes = buildMentionRegexes(cfg, routeAgentId, {
		provider: "telegram",
		conversationId: isGroup ? buildTelegramGroupPeerId(chatId, resolvedThreadId) : String(chatId),
		providerPolicy: providerMentionPatterns
	});
	const messageTextParts = getTelegramTextParts(msg);
	const allowForCommands = isGroup ? effectiveGroupAllow : effectiveDmAllow;
	const useAccessGroups = cfg.commands?.useAccessGroups !== false;
	const hasControlCommandInMessage = hasControlCommand(messageTextParts.text, cfg, { botUsername });
	const commandGate = await resolveTelegramCommandIngressAuthorization({
		accountId: accountId ?? "default",
		cfg,
		dmPolicy: "pairing",
		isGroup,
		chatId,
		resolvedThreadId,
		senderId,
		effectiveDmAllow,
		effectiveGroupAllow,
		ownerAccess: {
			ownerList: [],
			senderIsOwner: false
		},
		eventKind: "message",
		allowTextCommands: true,
		hasControlCommand: hasControlCommandInMessage,
		modeWhenAccessGroupsOff: "allow",
		includeDmAllowForGroupCommands: false
	});
	const commandAuthorized = commandGate.authorized;
	const historyKey = isGroup ? buildTelegramGroupPeerId(chatId, resolvedThreadId) : void 0;
	const originatingTo = providedOriginatingTo ?? buildTelegramInboundOriginTarget(chatId);
	const primaryMedia = resolveTelegramPrimaryMedia(msg);
	let placeholder = primaryMedia?.placeholder ?? "";
	const cachedStickerDescription = allMedia[0]?.stickerMetadata?.cachedDescription;
	const stickerSupportsVision = msg.sticker ? await resolveStickerVisionSupport$1({
		cfg,
		agentId: routeAgentId
	}) : false;
	const stickerCacheHit = Boolean(cachedStickerDescription) && !stickerSupportsVision;
	if (stickerCacheHit) {
		const emoji = allMedia[0]?.stickerMetadata?.emoji;
		const setName = allMedia[0]?.stickerMetadata?.setName;
		const stickerContext = [emoji, setName ? `from "${setName}"` : null].filter(Boolean).join(" ");
		placeholder = `[Sticker${stickerContext ? ` ${stickerContext}` : ""}] ${cachedStickerDescription}`;
	}
	const locationData = extractTelegramLocation(msg);
	const locationText = locationData ? formatLocationText(locationData) : void 0;
	const rawText = renderTelegramTextEntities(messageTextParts.text, messageTextParts.entities).trim();
	const hasUserText = Boolean(rawText || locationText);
	let rawBody = [rawText, locationText].filter(Boolean).join("\n").trim();
	if (!rawBody) rawBody = placeholder;
	if (!rawBody && allMedia.length === 0) return null;
	let bodyText = rawBody;
	if (allMedia.length === 0 && placeholder && rawBody !== placeholder) bodyText = `${primaryMedia?.fileRef.file_id ? `${placeholder} [file_id:${primaryMedia.fileRef.file_id}]` : placeholder}\n${bodyText}`.trim();
	const hasAudio = allMedia.some((media) => media.contentType?.startsWith("audio/"));
	const disableAudioPreflight = (topicConfig?.disableAudioPreflight ?? groupConfig?.disableAudioPreflight) === true;
	const senderAllowedForAudioPreflight = !useAccessGroups || !allowForCommands.hasEntries || commandAuthorized;
	let preflightTranscript;
	if (hasAudio && !hasUserText && (!isGroup || requireMention && mentionRegexes.length > 0 && !disableAudioPreflight && senderAllowedForAudioPreflight)) try {
		const { transcribeFirstAudio } = await loadMediaUnderstandingRuntime();
		preflightTranscript = await transcribeFirstAudio({
			ctx: {
				Provider: "telegram",
				Surface: "telegram",
				OriginatingChannel: "telegram",
				OriginatingTo: originatingTo,
				AccountId: accountId,
				MessageThreadId: replyThreadId,
				MediaPaths: allMedia.length > 0 ? allMedia.map((m) => m.path) : void 0,
				MediaTypes: allMedia.length > 0 ? allMedia.map((m) => m.contentType).filter(Boolean) : void 0
			},
			cfg,
			agentDir: void 0
		});
	} catch (err) {
		logVerbose(`telegram: audio preflight transcription failed: ${String(err)}`);
	}
	const audioTranscribedMediaIndex = preflightTranscript === void 0 ? void 0 : allMedia.findIndex((media) => media.contentType?.startsWith("audio/"));
	if (hasAudio && bodyText === "<media:audio>" && preflightTranscript) bodyText = formatAudioTranscriptForAgent(preflightTranscript);
	const savedMediaPlaceholder = formatSavedMediaPlaceholder(allMedia);
	if (!hasAudio && savedMediaPlaceholder && placeholder && bodyText === placeholder) bodyText = savedMediaPlaceholder;
	if (!bodyText && allMedia.length > 0) if (hasAudio) bodyText = preflightTranscript ? formatAudioTranscriptForAgent(preflightTranscript) : "<media:audio>";
	else bodyText = savedMediaPlaceholder ?? "<media:document>";
	const hasAnyMention = messageTextParts.entities.some((ent) => ent.type === "mention");
	const explicitlyMentioned = botUsername ? hasBotMention(msg, botUsername) : false;
	const computedWasMentioned = matchesMentionWithExplicit({
		text: messageTextParts.text,
		mentionRegexes,
		explicit: {
			hasAnyMention,
			isExplicitlyMentioned: explicitlyMentioned,
			canResolveExplicit: Boolean(botUsername)
		},
		transcript: preflightTranscript
	});
	const wasMentioned = options?.forceWasMentioned === true ? true : computedWasMentioned;
	if (isGroup && commandGate.shouldBlockControlCommand) {
		logInboundDrop({
			log: logVerbose,
			channel: "telegram",
			reason: "control command (unauthorized)",
			target: senderId ?? "unknown"
		});
		return null;
	}
	const botId = primaryCtx.me?.id;
	const replyFromId = msg.reply_to_message?.from?.id;
	const replyToBotMessage = botId != null && replyFromId === botId;
	const isReplyToServiceMessage = replyToBotMessage && isTelegramForumServiceMessage(msg.reply_to_message);
	const implicitMentionKinds = implicitMentionKindWhen("reply_to_bot", replyToBotMessage && !isReplyToServiceMessage);
	const canDetectMention = Boolean(botUsername) || mentionRegexes.length > 0;
	const mentionDecision = resolveInboundMentionDecision({
		facts: {
			canDetectMention,
			wasMentioned,
			hasAnyMention,
			implicitMentionKinds: isGroup ? implicitMentionKinds : []
		},
		policy: {
			isGroup,
			requireMention: Boolean(requireMention),
			allowTextCommands: true,
			hasControlCommand: hasControlCommandInMessage,
			commandAuthorized
		}
	});
	const effectiveWasMentioned = mentionDecision.effectiveWasMentioned;
	if (isGroup && requireMention && canDetectMention && mentionDecision.shouldSkip) {
		logger.info({
			chatId,
			reason: "no-mention"
		}, "skipping group message");
		createChannelHistoryWindow({ historyMap: groupHistories }).record({
			historyKey: historyKey ?? "",
			limit: historyLimit,
			entry: historyKey ? {
				sender: buildSenderLabel(msg, senderId || chatId),
				body: rawBody,
				timestamp: msg.date ? msg.date * 1e3 : void 0,
				messageId: typeof msg.message_id === "number" ? String(msg.message_id) : void 0
			} : null
		});
		const telegramGroupPolicy = resolveChannelGroupPolicy({
			cfg,
			channel: "telegram",
			groupId: String(chatId),
			accountId
		});
		if ((topicConfig?.ingest ?? telegramGroupPolicy.groupConfig?.ingest ?? telegramGroupPolicy.defaultConfig?.ingest) === true && sessionKey) fireAndForgetHook(triggerInternalHook(createInternalHookEvent("message", "received", sessionKey, toInternalMessageReceivedContext({
			from: `telegram:group:${historyKey ?? chatId}`,
			to: originatingTo,
			content: rawBody,
			timestamp: msg.date ? msg.date * 1e3 : void 0,
			channelId: "telegram",
			accountId,
			conversationId: originatingTo,
			messageId: typeof msg.message_id === "number" ? String(msg.message_id) : void 0,
			senderId: senderId || void 0,
			senderName: buildSenderName(msg),
			senderUsername: senderUsername || void 0,
			provider: "telegram",
			surface: "telegram",
			threadId: resolvedThreadId,
			originatingChannel: "telegram",
			originatingTo,
			isGroup: true,
			groupId: `telegram:${chatId}`
		}))), "telegram: mention-skip message hook failed");
		return null;
	}
	return {
		bodyText,
		rawBody,
		historyKey,
		commandAuthorized,
		effectiveWasMentioned,
		canDetectMention,
		shouldBypassMention: mentionDecision.shouldBypassMention,
		hasControlCommand: hasControlCommandInMessage,
		...audioTranscribedMediaIndex !== void 0 && audioTranscribedMediaIndex >= 0 ? { audioTranscribedMediaIndex } : {},
		stickerCacheHit,
		locationData: locationData ?? void 0
	};
}
//#endregion
//#region extensions/telegram/src/bot-message-context.session.ts
const sessionRuntimeMethods = [
	"buildChannelInboundEventContext",
	"readSessionUpdatedAt",
	"recordInboundSession",
	"resolveInboundLastRouteSessionKey",
	"resolvePinnedMainDmOwnerFromAllowlist",
	"resolveStorePath"
];
function hasCompleteSessionRuntime(runtime) {
	return Boolean(runtime && sessionRuntimeMethods.every((method) => typeof runtime[method] === "function"));
}
async function loadTelegramMessageContextSessionRuntime(runtime) {
	if (hasCompleteSessionRuntime(runtime)) return runtime;
	return {
		...await import("./bot-message-context.session.runtime.js"),
		...runtime
	};
}
async function resolveTelegramMessageContextStorePath(params) {
	return (await loadTelegramMessageContextSessionRuntime(params.sessionRuntime)).resolveStorePath(params.cfg.session?.store, { agentId: params.agentId });
}
function replyTargetToChainEntry(replyTarget) {
	return {
		...replyTarget.id ? { messageId: replyTarget.id } : {},
		sender: replyTarget.sender,
		...replyTarget.senderId ? { senderId: replyTarget.senderId } : {},
		...replyTarget.senderUsername ? { senderUsername: replyTarget.senderUsername } : {},
		...replyTarget.body ? { body: replyTarget.body } : {},
		...replyTarget.kind === "quote" ? { isQuote: true } : {},
		...replyTarget.forwardedFrom?.from ? { forwardedFrom: replyTarget.forwardedFrom.from } : {},
		...replyTarget.forwardedFrom?.fromId ? { forwardedFromId: replyTarget.forwardedFrom.fromId } : {},
		...replyTarget.forwardedFrom?.fromUsername ? { forwardedFromUsername: replyTarget.forwardedFrom.fromUsername } : {},
		...replyTarget.forwardedFrom?.date ? { forwardedDate: replyTarget.forwardedFrom.date * 1e3 } : {}
	};
}
function stripReplyChainForwarded(entry) {
	const { forwardedFrom: _forwardedFrom, forwardedFromId: _forwardedFromId, forwardedFromUsername: _forwardedFromUsername, forwardedDate: _forwardedDate, ...withoutForwarded } = entry;
	return withoutForwarded;
}
function formatReplyChainEntry(entry, index) {
	const forwardedAt = timestampMsToIsoString(entry.forwardedDate);
	const labels = [
		`${index + 1}. ${entry.sender ?? "unknown sender"}`,
		entry.messageId ? `id:${entry.messageId}` : void 0,
		entry.replyToId ? `reply_to:${entry.replyToId}` : void 0,
		entry.timestamp ? timestampMsToIsoString(entry.timestamp) : void 0
	].filter(Boolean);
	const bodyLines = [
		entry.forwardedFrom ? `[Forwarded from ${entry.forwardedFrom}${forwardedAt ? ` at ${forwardedAt}` : ""}]` : void 0,
		entry.isQuote && entry.body ? `"${entry.body}"` : entry.body,
		entry.mediaType ? `<media:${entry.mediaType}>` : void 0,
		entry.mediaPath ? `[media_path:${entry.mediaPath}]` : void 0,
		entry.mediaRef ? `[media_ref:${entry.mediaRef}]` : void 0
	].filter(Boolean);
	return `[${labels.join(" ")}]\n${bodyLines.join("\n")}`;
}
async function buildTelegramInboundContextPayload(params) {
	const { cfg, primaryCtx, msg, allMedia, replyMedia, replyChain, promptContext, isGroup, isForum, chatId, senderId, senderUsername, resolvedThreadId, dmThreadId, threadSpec, route, rawBody, bodyText, historyKey, historyLimit, groupHistories, groupConfig, topicConfig, stickerCacheHit, effectiveWasMentioned, hasControlCommand, audioTranscribedMediaIndex, commandAuthorized, locationData, options, dmAllowFrom, effectiveGroupAllow, topicName, sessionRuntime: sessionRuntimeOverride } = params;
	const replyTarget = describeReplyTarget(msg);
	const forwardOrigin = normalizeForwardedContext(msg);
	const contextVisibilityMode = resolveChannelContextVisibilityMode({
		cfg,
		channel: "telegram",
		accountId: route.accountId
	});
	const shouldIncludeGroupSupplementalContext = (paramsLocal) => {
		if (!isGroup) return true;
		const senderAllowed = effectiveGroupAllow?.hasEntries ? isSenderAllowed({
			allow: effectiveGroupAllow,
			senderId: paramsLocal.senderId,
			senderUsername: paramsLocal.senderUsername
		}) : true;
		return evaluateSupplementalContextVisibility({
			mode: contextVisibilityMode,
			kind: paramsLocal.kind,
			senderAllowed
		}).include;
	};
	const includeReplyTarget = replyTarget ? shouldIncludeGroupSupplementalContext({
		kind: "quote",
		senderId: replyTarget.senderId,
		senderUsername: replyTarget.senderUsername
	}) : false;
	const includeForwardOrigin = forwardOrigin ? shouldIncludeGroupSupplementalContext({
		kind: "forwarded",
		senderId: forwardOrigin.fromId,
		senderUsername: forwardOrigin.fromUsername
	}) : false;
	const visibleReplyForwardedFrom = includeReplyTarget && replyTarget?.forwardedFrom ? shouldIncludeGroupSupplementalContext({
		kind: "forwarded",
		senderId: replyTarget.forwardedFrom.fromId,
		senderUsername: replyTarget.forwardedFrom.fromUsername
	}) ? replyTarget.forwardedFrom : void 0 : void 0;
	const visibleReplyTarget = includeReplyTarget && replyTarget ? {
		...replyTarget,
		forwardedFrom: visibleReplyForwardedFrom
	} : null;
	const visibleReplyTargetEntry = visibleReplyTarget ? replyTargetToChainEntry(visibleReplyTarget) : void 0;
	const visibleReplyTargetById = new Map(visibleReplyTargetEntry?.messageId ? [[visibleReplyTargetEntry.messageId, visibleReplyTargetEntry]] : []);
	const visibleReplyChain = (replyChain.length > 0 ? replyChain : visibleReplyTargetEntry ? [visibleReplyTargetEntry] : []).flatMap((entry) => {
		const visibleEntry = {
			...entry,
			...entry.messageId ? visibleReplyTargetById.get(entry.messageId) : void 0
		};
		if (!shouldIncludeGroupSupplementalContext({
			kind: "quote",
			senderId: visibleEntry.senderId,
			senderUsername: visibleEntry.senderUsername
		})) return [];
		return [visibleEntry.forwardedFrom && shouldIncludeGroupSupplementalContext({
			kind: "forwarded",
			senderId: visibleEntry.forwardedFromId,
			senderUsername: visibleEntry.forwardedFromUsername
		}) ? visibleEntry : stripReplyChainForwarded(visibleEntry)];
	});
	const visibleForwardOrigin = includeForwardOrigin ? forwardOrigin : null;
	const visibleForwardOriginAt = timestampMsToIsoString(visibleForwardOrigin?.date ? visibleForwardOrigin.date * 1e3 : void 0);
	const replySuffix = visibleReplyChain.length > 0 ? `\n\n[Reply chain - nearest first]\n${visibleReplyChain.map(formatReplyChainEntry).join("\n")}\n[/Reply chain]` : "";
	const forwardPrefix = visibleForwardOrigin ? `[Forwarded from ${visibleForwardOrigin.from}${visibleForwardOriginAt ? ` at ${visibleForwardOriginAt}` : ""}]\n` : "";
	const groupLabel = isGroup ? buildGroupLabel(msg, chatId, resolvedThreadId) : void 0;
	const senderName = buildSenderName(msg);
	const conversationLabel = isGroup ? groupLabel ?? `group:${chatId}` : buildSenderLabel(msg, senderId || chatId);
	const sessionRuntime = await loadTelegramMessageContextSessionRuntime(sessionRuntimeOverride);
	const storePath = await resolveTelegramMessageContextStorePath({
		cfg,
		agentId: route.agentId,
		sessionRuntime: sessionRuntimeOverride
	});
	const envelopeOptions = resolveEnvelopeFormatOptions(cfg);
	const previousTimestamp = sessionRuntime.readSessionUpdatedAt({
		storePath,
		sessionKey: route.sessionKey
	});
	const body = formatInboundEnvelope({
		channel: "Telegram",
		from: conversationLabel,
		timestamp: msg.date ? msg.date * 1e3 : void 0,
		body: `${forwardPrefix}${bodyText}${replySuffix}`,
		chatType: isGroup ? "group" : "direct",
		sender: {
			name: senderName,
			username: senderUsername || void 0,
			id: senderId || void 0
		},
		previousTimestamp,
		envelope: envelopeOptions
	});
	const channelHistory = createChannelHistoryWindow({ historyMap: groupHistories });
	let combinedBody = body;
	if (isGroup && historyKey && historyLimit > 0) combinedBody = channelHistory.buildPendingContext({
		historyKey,
		limit: historyLimit,
		currentMessage: combinedBody,
		formatEntry: (entry) => formatInboundEnvelope({
			channel: "Telegram",
			from: groupLabel ?? `group:${chatId}`,
			timestamp: entry.timestamp,
			body: `${entry.body} [id:${entry.messageId ?? "unknown"} chat:${chatId}]`,
			chatType: "group",
			senderLabel: entry.sender,
			envelope: envelopeOptions
		})
	});
	const { skillFilter, groupSystemPrompt } = resolveTelegramGroupPromptSettings({
		groupConfig,
		topicConfig
	});
	const commandBody = normalizeCommandBody(rawBody, { botUsername: normalizeOptionalLowercaseString(primaryCtx.me?.username) });
	const inboundHistory = isGroup && historyKey && historyLimit > 0 ? channelHistory.buildInboundHistory({
		historyKey,
		limit: historyLimit
	}) : void 0;
	const currentMediaForContext = stickerCacheHit ? [] : allMedia;
	const replyHead = visibleReplyChain[0];
	const toInboundMedia = (media, index) => ({
		path: media.path,
		url: media.path,
		contentType: media.contentType,
		transcribed: index !== void 0 && audioTranscribedMediaIndex === index
	});
	const currentMediaFacts = currentMediaForContext.map(toInboundMedia);
	const replyMediaFacts = visibleReplyChain.length > 0 ? visibleReplyChain.flatMap((entry) => entry.mediaPath ? [{
		path: entry.mediaPath,
		url: entry.mediaPath,
		contentType: entry.mediaType
	}] : []) : visibleReplyTarget ? replyMedia.map((media) => toInboundMedia(media)) : [];
	const telegramFrom = isGroup ? buildTelegramGroupFrom(chatId, resolvedThreadId) : `telegram:${chatId}`;
	const telegramTo = buildTelegramInboundOriginTarget(chatId, threadSpec);
	const locationContext = locationData ? toLocationContext(locationData) : void 0;
	const commandSource = options?.commandSource ?? (commandAuthorized && hasControlCommand ? "text" : void 0);
	const unmentionedGroupPolicy = resolveUnmentionedGroupInboundPolicy({
		cfg,
		agentId: route.agentId
	});
	const hasAbortRequest = isAbortRequestText(rawBody, { botUsername: normalizeOptionalLowercaseString(primaryCtx.me?.username) });
	const conversationKind = isGroup ? "group" : "direct";
	const inboundEventKind = classifyChannelInboundEvent({
		conversation: { kind: conversationKind },
		unmentionedGroupPolicy,
		wasMentioned: effectiveWasMentioned,
		hasControlCommand,
		hasAbortRequest,
		commandSource
	});
	const ctxPayload = await sessionRuntime.buildChannelInboundEventContext({
		channel: "telegram",
		resolveSupplementalMedia: true,
		accountId: route.accountId,
		messageId: options?.messageIdOverride ?? String(msg.message_id),
		timestamp: msg.date ? msg.date * 1e3 : void 0,
		from: telegramFrom,
		sender: {
			...senderId ? { id: senderId } : {},
			name: senderName,
			username: senderUsername || void 0
		},
		conversation: {
			kind: conversationKind,
			id: String(chatId),
			label: conversationLabel,
			threadId: threadSpec.id != null ? String(threadSpec.id) : void 0
		},
		route: {
			agentId: route.agentId,
			accountId: route.accountId,
			routeSessionKey: route.sessionKey,
			mainSessionKey: route.mainSessionKey
		},
		reply: {
			to: telegramTo,
			replyToId: replyHead?.messageId ?? visibleReplyTarget?.id,
			messageThreadId: threadSpec.id
		},
		message: {
			inboundEventKind,
			body: combinedBody,
			rawBody,
			bodyForAgent: bodyText,
			commandBody,
			inboundHistory
		},
		access: { commands: { authorized: commandAuthorized } },
		command: commandSource === "native" ? {
			kind: "native",
			authorized: commandAuthorized,
			body: commandBody
		} : commandSource === "text" ? {
			kind: "text-slash",
			authorized: commandAuthorized,
			body: commandBody
		} : void 0,
		media: currentMediaFacts,
		supplemental: {
			quote: replyHead || visibleReplyTarget ? {
				id: replyHead?.messageId ?? visibleReplyTarget?.id,
				body: replyHead?.body ?? visibleReplyTarget?.body,
				sender: replyHead?.sender ?? visibleReplyTarget?.sender,
				senderAllowed: true,
				isQuote: replyHead?.isQuote ?? (visibleReplyTarget?.kind === "quote" ? true : void 0),
				media: replyMediaFacts
			} : void 0,
			forwarded: visibleForwardOrigin ? {
				from: visibleForwardOrigin.from,
				fromType: visibleForwardOrigin.fromType,
				fromId: visibleForwardOrigin.fromId,
				date: visibleForwardOrigin.date ? visibleForwardOrigin.date * 1e3 : void 0,
				senderAllowed: true
			} : void 0,
			groupSystemPrompt: isGroup || !isGroup && groupConfig ? groupSystemPrompt : void 0,
			untrustedContext: promptContext.length > 0 ? promptContext : void 0
		},
		contextVisibility: contextVisibilityMode,
		extra: {
			BotUsername: primaryCtx.me?.username ?? void 0,
			GroupSubject: isGroup ? msg.chat.title ?? void 0 : void 0,
			ReplyChain: visibleReplyChain.length > 0 ? visibleReplyChain : void 0,
			ReplyToIsExternal: visibleReplyTarget?.source === "external_reply" ? true : void 0,
			ReplyToQuoteText: visibleReplyTarget?.quoteText,
			ReplyToQuotePosition: visibleReplyTarget?.quotePosition,
			ReplyToQuoteEntities: visibleReplyTarget?.quoteEntities,
			ReplyToQuoteSourceText: visibleReplyTarget?.quoteSourceText,
			ReplyToQuoteSourceEntities: visibleReplyTarget?.quoteSourceEntities,
			ReplyToForwardedFrom: visibleReplyTarget?.forwardedFrom?.from,
			ReplyToForwardedFromType: visibleReplyTarget?.forwardedFrom?.fromType,
			ReplyToForwardedFromId: visibleReplyTarget?.forwardedFrom?.fromId,
			ReplyToForwardedFromUsername: visibleReplyTarget?.forwardedFrom?.fromUsername,
			ReplyToForwardedFromTitle: visibleReplyTarget?.forwardedFrom?.fromTitle,
			ReplyToForwardedDate: visibleReplyTarget?.forwardedFrom?.date ? visibleReplyTarget.forwardedFrom.date * 1e3 : void 0,
			ForwardedFromUsername: visibleForwardOrigin?.fromUsername,
			ForwardedFromTitle: visibleForwardOrigin?.fromTitle,
			ForwardedFromSignature: visibleForwardOrigin?.fromSignature,
			ForwardedFromChatType: visibleForwardOrigin?.fromChatType,
			ForwardedFromMessageId: visibleForwardOrigin?.fromMessageId,
			WasMentioned: isGroup ? effectiveWasMentioned : void 0,
			Sticker: allMedia[0]?.stickerMetadata,
			StickerMediaIncluded: allMedia[0]?.stickerMetadata ? !stickerCacheHit : void 0,
			...locationContext,
			IsForum: isForum,
			TopicName: isForum && topicName ? topicName : void 0
		}
	});
	if (inboundEventKind === "room_event" && historyKey) channelHistory.record({
		historyKey,
		limit: historyLimit,
		entry: {
			sender: buildSenderLabel(msg, senderId || chatId),
			body: rawBody,
			timestamp: msg.date ? msg.date * 1e3 : void 0,
			messageId: typeof msg.message_id === "number" ? String(msg.message_id) : void 0
		}
	});
	const pinnedMainDmOwner = !isGroup ? sessionRuntime.resolvePinnedMainDmOwnerFromAllowlist({
		dmScope: cfg.session?.dmScope,
		allowFrom: dmAllowFrom,
		normalizeEntry: (entry) => normalizeAllowFrom([entry]).entries[0]
	}) : null;
	const updateLastRouteSessionKey = sessionRuntime.resolveInboundLastRouteSessionKey({
		route,
		sessionKey: route.sessionKey
	});
	const shouldPersistGroupLastRouteThread = isGroup && route.matchedBy !== "binding.channel";
	const updateLastRouteThreadId = isGroup ? shouldPersistGroupLastRouteThread && resolvedThreadId != null ? String(resolvedThreadId) : void 0 : dmThreadId != null ? String(dmThreadId) : void 0;
	const updateLastRoute = !isGroup || updateLastRouteThreadId != null ? {
		sessionKey: updateLastRouteSessionKey,
		channel: "telegram",
		to: isGroup && updateLastRouteThreadId != null ? `telegram:${chatId}:topic:${updateLastRouteThreadId}` : `telegram:${chatId}`,
		accountId: route.accountId,
		threadId: updateLastRouteThreadId,
		mainDmOwnerPin: !isGroup && updateLastRouteSessionKey === route.mainSessionKey && pinnedMainDmOwner && senderId ? {
			ownerRecipient: pinnedMainDmOwner,
			senderRecipient: senderId,
			onSkip: (skipParams) => {
				logVerbose(`telegram: skip main-session last route for ${skipParams.senderRecipient} (pinned owner ${skipParams.ownerRecipient})`);
			}
		} : void 0
	} : void 0;
	if (visibleReplyTarget && shouldLogVerbose()) {
		const preview = (visibleReplyTarget.body ?? "").replace(/\s+/g, " ").slice(0, 120);
		logVerbose(`telegram reply-context: replyToId=${visibleReplyTarget.id} replyToSender=${visibleReplyTarget.sender} replyToBody="${preview}"`);
	}
	if (visibleForwardOrigin && shouldLogVerbose()) logVerbose(`telegram forward-context: forwardedFrom="${visibleForwardOrigin.from}" type=${visibleForwardOrigin.fromType}`);
	if (shouldLogVerbose()) {
		const preview = body.slice(0, 200).replace(/\n/g, "\\n");
		const mediaInfo = allMedia.length > 1 ? ` mediaCount=${allMedia.length}` : "";
		const topicInfo = resolvedThreadId != null ? ` topic=${resolvedThreadId}` : "";
		logVerbose(`telegram inbound: chatId=${chatId} from=${ctxPayload.From} len=${body.length}${mediaInfo}${topicInfo} preview="${preview}"`);
	}
	return {
		ctxPayload,
		skillFilter,
		turn: {
			storePath,
			recordInboundSession: sessionRuntime.recordInboundSession,
			record: {
				updateLastRoute,
				onRecordError: (err) => {
					logVerbose(`telegram: failed updating session meta: ${String(err)}`);
				}
			}
		}
	};
}
//#endregion
//#region extensions/telegram/src/status-reaction-variants.ts
const TELEGRAM_GENERIC_REACTION_FALLBACKS = [
	"👍",
	"👀",
	"🔥"
];
const TELEGRAM_SUPPORTED_REACTION_EMOJIS = new Set([
	"❤",
	"👍",
	"👎",
	"🔥",
	"🥰",
	"👏",
	"😁",
	"🤔",
	"🤯",
	"😱",
	"🤬",
	"😢",
	"🎉",
	"🤩",
	"🤮",
	"💩",
	"🙏",
	"👌",
	"🕊",
	"🤡",
	"🥱",
	"🥴",
	"😍",
	"🐳",
	"❤‍🔥",
	"🌚",
	"🌭",
	"💯",
	"🤣",
	"⚡",
	"🍌",
	"🏆",
	"💔",
	"🤨",
	"😐",
	"🍓",
	"🍾",
	"💋",
	"🖕",
	"😈",
	"😴",
	"😭",
	"🤓",
	"👻",
	"👨‍💻",
	"👀",
	"🎃",
	"🙈",
	"😇",
	"😨",
	"🤝",
	"✍",
	"🤗",
	"🫡",
	"🎅",
	"🎄",
	"☃",
	"💅",
	"🤪",
	"🗿",
	"🆒",
	"💘",
	"🙉",
	"🦄",
	"😘",
	"💊",
	"🙊",
	"😎",
	"👾",
	"🤷‍♂",
	"🤷",
	"🤷‍♀",
	"😡"
]);
const TELEGRAM_STATUS_REACTION_VARIANTS = {
	queued: [
		"👀",
		"👍",
		"🔥"
	],
	thinking: [
		"🤔",
		"🤓",
		"👀"
	],
	tool: [
		"🔥",
		"⚡",
		"👍"
	],
	coding: [
		"👨‍💻",
		"🔥",
		"⚡"
	],
	web: [
		"⚡",
		"🔥",
		"👍"
	],
	deploy: [
		"🔥",
		"⚡",
		"👍"
	],
	build: [
		"🔥",
		"👨‍💻",
		"⚡"
	],
	concierge: [
		"👀",
		"🔥",
		"⚡"
	],
	done: [
		"👍",
		"🎉",
		"💯"
	],
	error: [
		"😱",
		"😨",
		"🤯"
	],
	stallSoft: [
		"🥱",
		"😴",
		"🤔"
	],
	stallHard: [
		"😨",
		"😱",
		"⚡"
	],
	compacting: [
		"✍",
		"🤔",
		"🤯"
	]
};
const STATUS_REACTION_EMOJI_KEYS = [
	"queued",
	"thinking",
	"tool",
	"coding",
	"web",
	"deploy",
	"build",
	"concierge",
	"done",
	"error",
	"stallSoft",
	"stallHard",
	"compacting"
];
function toUniqueNonEmpty(values) {
	return uniqueStrings(normalizeStringEntries(values));
}
function resolveTelegramStatusReactionEmojis(params) {
	const { overrides } = params;
	const queuedFallback = normalizeOptionalString(params.initialEmoji) ?? DEFAULT_EMOJIS.queued;
	return {
		queued: normalizeOptionalString(overrides?.queued) ?? queuedFallback,
		thinking: normalizeOptionalString(overrides?.thinking) ?? DEFAULT_EMOJIS.thinking,
		tool: normalizeOptionalString(overrides?.tool) ?? DEFAULT_EMOJIS.tool,
		coding: normalizeOptionalString(overrides?.coding) ?? DEFAULT_EMOJIS.coding,
		web: normalizeOptionalString(overrides?.web) ?? DEFAULT_EMOJIS.web,
		deploy: normalizeOptionalString(overrides?.deploy) ?? DEFAULT_EMOJIS.deploy,
		build: normalizeOptionalString(overrides?.build) ?? DEFAULT_EMOJIS.build,
		concierge: normalizeOptionalString(overrides?.concierge) ?? DEFAULT_EMOJIS.concierge,
		done: normalizeOptionalString(overrides?.done) ?? DEFAULT_EMOJIS.done,
		error: normalizeOptionalString(overrides?.error) ?? DEFAULT_EMOJIS.error,
		stallSoft: normalizeOptionalString(overrides?.stallSoft) ?? DEFAULT_EMOJIS.stallSoft,
		stallHard: normalizeOptionalString(overrides?.stallHard) ?? DEFAULT_EMOJIS.stallHard,
		compacting: normalizeOptionalString(overrides?.compacting) ?? DEFAULT_EMOJIS.compacting
	};
}
function buildTelegramStatusReactionVariants(emojis) {
	const variantsByRequested = /* @__PURE__ */ new Map();
	for (const key of STATUS_REACTION_EMOJI_KEYS) {
		const requested = normalizeOptionalString(emojis[key]);
		if (!requested) continue;
		const candidates = toUniqueNonEmpty([requested, ...TELEGRAM_STATUS_REACTION_VARIANTS[key] ?? []]);
		variantsByRequested.set(requested, candidates);
	}
	return variantsByRequested;
}
function isTelegramSupportedReactionEmoji(emoji) {
	return TELEGRAM_SUPPORTED_REACTION_EMOJIS.has(emoji);
}
function extractTelegramAllowedEmojiReactions(chat) {
	if (!chat) return;
	const availableReactions = chat.available_reactions;
	if (availableReactions === void 0) return;
	if (availableReactions == null) return null;
	if (!Array.isArray(availableReactions)) return /* @__PURE__ */ new Set();
	const allowed = /* @__PURE__ */ new Set();
	for (const reaction of availableReactions) {
		if (reaction.type !== "emoji") continue;
		const emoji = reaction.emoji.trim();
		if (emoji && isTelegramSupportedReactionEmoji(emoji)) allowed.add(emoji);
	}
	return allowed;
}
async function resolveTelegramAllowedEmojiReactions(params) {
	const fromMessage = extractTelegramAllowedEmojiReactions(params.chat);
	if (fromMessage !== void 0) return fromMessage;
	if (params.getChat) try {
		const fromLookup = extractTelegramAllowedEmojiReactions(await params.getChat(params.chatId));
		if (fromLookup !== void 0) return fromLookup;
	} catch {
		return null;
	}
	return null;
}
function resolveTelegramReactionVariant(params) {
	const requestedEmoji = normalizeOptionalString(params.requestedEmoji);
	if (!requestedEmoji) return;
	const variants = toUniqueNonEmpty([...params.variantsByRequestedEmoji.get(requestedEmoji) ?? [requestedEmoji], ...TELEGRAM_GENERIC_REACTION_FALLBACKS]);
	for (const candidate of variants) {
		if (!isTelegramSupportedReactionEmoji(candidate)) continue;
		if (params.allowedEmojiReactions == null || params.allowedEmojiReactions.has(candidate)) return candidate;
	}
}
//#endregion
//#region extensions/telegram/src/bot-message-context.ts
let telegramMessageContextRuntimePromise;
async function loadTelegramMessageContextRuntime() {
	telegramMessageContextRuntimePromise ??= import("./bot-message-context.runtime.js");
	return await telegramMessageContextRuntimePromise;
}
const buildTelegramMessageContext = async ({ primaryCtx, allMedia, replyMedia = [], replyChain = [], promptContext = [], storeAllowFrom, options, bot, cfg, account, historyLimit, groupHistories, dmPolicy, allowFrom, groupAllowFrom, ackReactionScope, logger, resolveGroupActivation, resolveGroupRequireMention, resolveTelegramGroupConfig, loadFreshConfig, runtime, sessionRuntime, upsertPairingRequest, sendChatActionHandler }) => {
	const msg = primaryCtx.message;
	const chatId = msg.chat.id;
	const isGroup = msg.chat.type === "group" || msg.chat.type === "supergroup";
	const senderId = msg.from?.id ? String(msg.from.id) : "";
	const messageThreadId = msg.message_thread_id;
	const reactionApi = typeof bot.api.setMessageReaction === "function" ? bot.api.setMessageReaction.bind(bot.api) : null;
	const getChatApi = typeof bot.api.getChat === "function" ? bot.api.getChat.bind(bot.api) : void 0;
	const isForum = await resolveTelegramForumFlag({
		chatId,
		chatType: msg.chat.type,
		isGroup,
		isForum: extractTelegramForumFlag(msg.chat),
		isTopicMessage: msg.is_topic_message,
		getChat: getChatApi
	});
	const threadSpec = resolveTelegramThreadSpec({
		isGroup,
		isForum,
		messageThreadId
	});
	const resolvedThreadId = threadSpec.scope === "forum" ? threadSpec.id : void 0;
	const replyThreadId = threadSpec.id;
	const dmThreadId = threadSpec.scope === "dm" ? threadSpec.id : void 0;
	let topicName;
	if (isForum && resolvedThreadId != null) {
		const topicNameCacheScope = resolveTopicNameCacheScope(await resolveTelegramMessageContextStorePath({
			cfg,
			agentId: account.accountId,
			sessionRuntime
		}));
		const ftCreated = msg.forum_topic_created;
		const ftEdited = msg.forum_topic_edited;
		const ftClosed = msg.forum_topic_closed;
		const ftReopened = msg.forum_topic_reopened;
		const topicPatch = ftCreated?.name ? {
			name: ftCreated.name,
			iconColor: ftCreated.icon_color,
			iconCustomEmojiId: ftCreated.icon_custom_emoji_id,
			closed: false
		} : ftEdited?.name ? {
			name: ftEdited.name,
			iconCustomEmojiId: ftEdited.icon_custom_emoji_id
		} : ftClosed ? { closed: true } : ftReopened ? { closed: false } : void 0;
		if (topicPatch) await updateTopicName(chatId, resolvedThreadId, topicPatch, topicNameCacheScope);
		topicName = await getTopicName(chatId, resolvedThreadId, topicNameCacheScope);
		if (!topicName) {
			const replyFtCreated = msg.reply_to_message?.forum_topic_created;
			if (replyFtCreated?.name) {
				await updateTopicName(chatId, resolvedThreadId, {
					name: replyFtCreated.name,
					iconColor: replyFtCreated.icon_color,
					iconCustomEmojiId: replyFtCreated.icon_custom_emoji_id
				}, topicNameCacheScope);
				topicName = replyFtCreated.name;
			}
		}
	}
	const { groupConfig, topicConfig } = resolveTelegramGroupConfig(chatId, resolvedThreadId ?? dmThreadId);
	const directConfig = !isGroup ? groupConfig : void 0;
	const telegramGroupConfig = isGroup ? groupConfig : void 0;
	const effectiveDmPolicy = resolveTelegramEffectiveDmPolicy({
		isGroup,
		groupConfig,
		dmPolicy
	});
	const freshCfg = loadFreshConfig?.() ?? (runtime?.getRuntimeConfig ?? (await loadTelegramMessageContextRuntime()).getRuntimeConfig)();
	const conversationRoute = resolveTelegramConversationRoute({
		cfg: freshCfg,
		accountId: account.accountId,
		chatId,
		isGroup,
		resolvedThreadId,
		replyThreadId,
		senderId,
		topicAgentId: topicConfig?.agentId
	});
	const { bindingMode } = conversationRoute;
	let { route } = conversationRoute;
	const requiresExplicitAccountBinding = (candidate) => normalizeAccountId(candidate.accountId) !== normalizeAccountId(resolveDefaultTelegramAccountId(freshCfg)) && candidate.matchedBy === "default";
	const isNamedAccountFallback = requiresExplicitAccountBinding(route);
	const hasExplicitTopicRoute = isGroup && Boolean(topicConfig?.agentId?.trim());
	if (isNamedAccountFallback && isGroup && !hasExplicitTopicRoute) {
		logInboundDrop({
			log: logVerbose,
			channel: "telegram",
			reason: "non-default account requires explicit binding",
			target: route.accountId
		});
		return null;
	}
	const groupAllowOverride = firstDefined(topicConfig?.allowFrom, groupConfig?.allowFrom);
	const dmAllow = await resolveTelegramDmAllow({
		cfg: freshCfg,
		groupAllowOverride,
		allowFrom,
		accountId: account.accountId,
		senderId,
		storeAllowFrom,
		dmPolicy: effectiveDmPolicy
	});
	const effectiveGroupAllow = normalizeAllowFrom(await expandTelegramAllowFromWithAccessGroups({
		cfg: freshCfg,
		allowFrom: groupAllowOverride ?? groupAllowFrom,
		accountId: account.accountId,
		senderId
	}));
	const hasGroupAllowOverride = groupAllowOverride !== void 0;
	const senderUsername = msg.from?.username ?? "";
	const baseAccess = evaluateTelegramGroupBaseAccess({
		isGroup,
		groupConfig,
		topicConfig,
		hasGroupAllowOverride,
		effectiveGroupAllow,
		senderId,
		senderUsername,
		enforceAllowOverride: true,
		requireSenderForAllowOverride: false
	});
	if (!baseAccess.allowed) {
		if (baseAccess.reason === "group-disabled") {
			logVerbose(`Blocked telegram group ${chatId} (group disabled)`);
			return null;
		}
		if (baseAccess.reason === "topic-disabled") {
			logVerbose(`Blocked telegram topic ${chatId} (${resolvedThreadId ?? "unknown"}) (topic disabled)`);
			return null;
		}
		logVerbose(isGroup ? `Blocked telegram group sender ${senderId || "unknown"} (group allowFrom override)` : `Blocked telegram DM sender ${senderId || "unknown"} (DM allowFrom override)`);
		return null;
	}
	const requireTopic = directConfig?.requireTopic;
	if (!isGroup && requireTopic === true && dmThreadId == null) {
		logVerbose(`Blocked telegram DM ${chatId}: requireTopic=true but no topic present`);
		return null;
	}
	const sendTyping = async () => {
		await withTelegramApiErrorLogging({
			operation: "sendChatAction",
			fn: () => sendChatActionHandler.sendChatAction(chatId, "typing", buildTypingThreadParams(replyThreadId))
		});
	};
	const sendRecordVoice = async () => {
		try {
			await withTelegramApiErrorLogging({
				operation: "sendChatAction",
				fn: () => sendChatActionHandler.sendChatAction(chatId, "record_voice", buildTypingThreadParams(replyThreadId))
			});
		} catch (err) {
			logVerbose(`telegram record_voice cue failed for chat ${chatId}: ${String(err)}`);
		}
	};
	if (!await enforceTelegramDmAccess({
		isGroup,
		dmPolicy: effectiveDmPolicy,
		msg,
		chatId,
		effectiveDmAllow: dmAllow.effectiveAllow,
		accountId: account.accountId,
		bot,
		logger,
		upsertPairingRequest
	})) return null;
	let initialTypingCueSent = false;
	const ensureConfiguredBindingReady = async () => {
		if (bindingMode.kind !== "configured") return true;
		const ensured = await (runtime?.ensureConfiguredBindingRouteReady ?? (await loadTelegramMessageContextRuntime()).ensureConfiguredBindingRouteReady)({
			cfg: freshCfg,
			bindingResolution: bindingMode.binding
		});
		if (ensured.ok) {
			logVerbose(`telegram: using configured ACP binding for ${bindingMode.binding.record.conversation.conversationId} -> ${bindingMode.sessionKey}`);
			return true;
		}
		logVerbose(`telegram: configured ACP binding unavailable for ${bindingMode.binding.record.conversation.conversationId}: ${ensured.error}`);
		logInboundDrop({
			log: logVerbose,
			channel: "telegram",
			reason: "configured ACP binding unavailable",
			target: bindingMode.binding.record.conversation.conversationId
		});
		return false;
	};
	const baseSessionKey = resolveTelegramConversationBaseSessionKey({
		cfg: freshCfg,
		route,
		chatId,
		isGroup,
		senderId
	});
	const sessionKey = (shouldUseTelegramDmThreadSession({
		dmThreadId,
		botHasTopicsEnabled: resolveTelegramBotHasTopicsEnabled(primaryCtx.me)
	}) && dmThreadId != null ? resolveThreadSessionKeys({
		baseSessionKey,
		threadId: `${chatId}:${dmThreadId}`
	}) : null)?.sessionKey ?? baseSessionKey;
	route = {
		...route,
		sessionKey,
		lastRoutePolicy: deriveLastRoutePolicy({
			sessionKey,
			mainSessionKey: route.mainSessionKey
		})
	};
	const activationOverride = resolveGroupActivation({
		chatId,
		messageThreadId: resolvedThreadId,
		sessionKey,
		agentId: route.agentId
	});
	const baseRequireMention = resolveGroupRequireMention(chatId);
	const requireMention = isGroup && bindingMode.kind === "plugin-owned-runtime" ? false : firstDefined(topicConfig?.requireMention, activationOverride, telegramGroupConfig?.requireMention, baseRequireMention);
	(runtime?.recordChannelActivity ?? (await loadTelegramMessageContextRuntime()).recordChannelActivity)({
		channel: "telegram",
		accountId: account.accountId,
		direction: "inbound"
	});
	const originatingTo = buildTelegramInboundOriginTarget(chatId, threadSpec);
	const bodyResult = await resolveTelegramInboundBody({
		cfg,
		primaryCtx,
		msg,
		allMedia,
		isGroup,
		chatId,
		accountId: account.accountId,
		senderId,
		senderUsername,
		resolvedThreadId,
		replyThreadId,
		originatingTo,
		routeAgentId: route.agentId,
		sessionKey,
		effectiveGroupAllow,
		effectiveDmAllow: dmAllow.effectiveAllow,
		groupConfig,
		topicConfig,
		providerMentionPatterns: cfg.channels?.telegram?.accounts?.[account.accountId]?.mentionPatterns,
		requireMention,
		options,
		groupHistories,
		historyLimit,
		logger
	});
	if (!bodyResult) return null;
	if (!await ensureConfiguredBindingReady()) return null;
	if (!isGroup) {
		initialTypingCueSent = true;
		sendTyping().catch((err) => {
			logVerbose(`telegram early direct typing cue failed for chat ${chatId}: ${String(err)}`);
		});
	}
	const { ctxPayload, skillFilter, turn } = await buildTelegramInboundContextPayload({
		cfg,
		primaryCtx,
		msg,
		allMedia,
		replyMedia,
		replyChain,
		promptContext,
		isGroup,
		isForum,
		chatId,
		senderId,
		senderUsername,
		resolvedThreadId,
		dmThreadId,
		threadSpec,
		route,
		rawBody: bodyResult.rawBody,
		bodyText: bodyResult.bodyText,
		historyKey: bodyResult.historyKey ?? "",
		historyLimit,
		groupHistories,
		groupConfig,
		topicConfig,
		stickerCacheHit: bodyResult.stickerCacheHit,
		effectiveWasMentioned: bodyResult.effectiveWasMentioned,
		hasControlCommand: bodyResult.hasControlCommand,
		...bodyResult.audioTranscribedMediaIndex !== void 0 ? { audioTranscribedMediaIndex: bodyResult.audioTranscribedMediaIndex } : {},
		locationData: bodyResult.locationData,
		options,
		dmAllowFrom: dmAllow.allowFrom,
		effectiveGroupAllow,
		commandAuthorized: bodyResult.commandAuthorized,
		topicName,
		sessionRuntime
	});
	const canShowStatusReaction = ctxPayload.InboundEventKind !== "room_event";
	const ackReaction = resolveAckReaction(cfg, route.agentId, {
		channel: "telegram",
		accountId: account.accountId
	});
	const ackReactionEmoji = ackReaction && isTelegramSupportedReactionEmoji(ackReaction) ? ackReaction : void 0;
	const removeAckAfterReply = cfg.messages?.removeAckAfterReply ?? false;
	const shouldSendAckReaction = Boolean(canShowStatusReaction && ackReaction && shouldAckReaction({
		scope: ackReactionScope,
		isDirect: !isGroup,
		isGroup,
		isMentionableGroup: isGroup,
		requireMention: Boolean(requireMention),
		canDetectMention: bodyResult.canDetectMention,
		effectiveWasMentioned: bodyResult.effectiveWasMentioned,
		shouldBypassMention: bodyResult.shouldBypassMention
	}));
	const statusReactionsConfig = cfg.messages?.statusReactions;
	const statusReactionsEnabled = statusReactionsConfig?.enabled === true && Boolean(reactionApi) && shouldSendAckReaction;
	const resolvedStatusReactionEmojis = statusReactionsEnabled ? resolveTelegramStatusReactionEmojis({
		initialEmoji: ackReaction,
		overrides: statusReactionsConfig?.emojis
	}) : null;
	const statusReactionVariantsByEmoji = resolvedStatusReactionEmojis ? buildTelegramStatusReactionVariants(resolvedStatusReactionEmojis) : /* @__PURE__ */ new Map();
	let allowedStatusReactionEmojisPromise = null;
	const createStatusReactionController = statusReactionsEnabled && resolvedStatusReactionEmojis && msg.message_id ? runtime?.createStatusReactionController ?? (await loadTelegramMessageContextRuntime()).createStatusReactionController : null;
	const statusReactionController = createStatusReactionController ? createStatusReactionController({
		enabled: true,
		adapter: { setReaction: async (emoji) => {
			if (reactionApi) {
				if (!allowedStatusReactionEmojisPromise) allowedStatusReactionEmojisPromise = resolveTelegramAllowedEmojiReactions({
					chat: msg.chat,
					chatId,
					getChat: getChatApi ?? void 0
				}).catch((err) => {
					logVerbose(`telegram status-reaction available_reactions lookup failed for chat ${chatId}: ${String(err)}`);
					return null;
				});
				const resolvedEmoji = resolveTelegramReactionVariant({
					requestedEmoji: emoji,
					variantsByRequestedEmoji: statusReactionVariantsByEmoji,
					allowedEmojiReactions: await allowedStatusReactionEmojisPromise
				});
				if (!resolvedEmoji) return;
				await reactionApi(chatId, msg.message_id, [{
					type: "emoji",
					emoji: resolvedEmoji
				}]);
			}
		} },
		initialEmoji: ackReaction,
		emojis: resolvedStatusReactionEmojis ?? void 0,
		timing: statusReactionsConfig?.timing,
		onError: (err) => {
			logVerbose(`telegram status-reaction error for chat ${chatId}: ${String(err)}`);
		}
	}) : null;
	const ackReactionPromise = statusReactionController ? shouldSendAckReaction ? Promise.resolve(statusReactionController.setQueued()).then(() => true, () => false) : null : shouldSendAckReaction && msg.message_id && reactionApi && ackReactionEmoji ? withTelegramApiErrorLogging({
		operation: "setMessageReaction",
		fn: () => reactionApi(chatId, msg.message_id, [{
			type: "emoji",
			emoji: ackReactionEmoji
		}])
	}).then(() => true, (err) => {
		logVerbose(`telegram react failed for chat ${chatId}: ${String(err)}`);
		return false;
	}) : null;
	return {
		ctxPayload,
		turn,
		primaryCtx,
		msg,
		chatId,
		isGroup,
		groupConfig,
		topicConfig,
		resolvedThreadId,
		threadSpec,
		replyThreadId,
		isForum,
		historyKey: bodyResult.historyKey ?? "",
		historyLimit,
		groupHistories,
		route,
		skillFilter,
		sendTyping,
		sendRecordVoice,
		sendChatActionHandler,
		initialTypingCueSent,
		ackReactionPromise,
		reactionApi,
		removeAckAfterReply,
		statusReactionController,
		accountId: account.accountId
	};
};
//#endregion
//#region extensions/telegram/src/agent-config.ts
const DEFAULT_AGENT_ID = "main";
function normalizeAgentId(value) {
	return (value ?? "").trim().toLowerCase() || DEFAULT_AGENT_ID;
}
function resolveTelegramConfigReasoningDefault(cfg, agentId) {
	const id = normalizeAgentId(agentId);
	return cfg.agents?.list?.find((entry) => normalizeAgentId(entry?.id) === id)?.reasoningDefault ?? cfg.agents?.defaults?.reasoningDefault ?? "off";
}
//#endregion
//#region extensions/telegram/src/bot-message-dispatch.media-dedup.ts
function deduplicateBlockSentMedia(payload, sentBlockMediaUrls) {
	if (!payload.mediaUrls?.length || sentBlockMediaUrls.size === 0) return payload;
	const remainingMedia = payload.mediaUrls.filter((url) => !sentBlockMediaUrls.has(url));
	if (remainingMedia.length === payload.mediaUrls.length) return payload;
	if (remainingMedia.length === 0 && !payload.text) return;
	return {
		...payload,
		mediaUrls: remainingMedia,
		mediaUrl: remainingMedia.length === 0 ? void 0 : payload.mediaUrl
	};
}
//#endregion
//#region extensions/telegram/src/bot-message-dispatch.media.ts
function pruneStickerMediaFromContext(ctxPayload, opts) {
	if (opts?.stickerMediaIncluded === false) return;
	const nextMediaPaths = Array.isArray(ctxPayload.MediaPaths) ? ctxPayload.MediaPaths.slice(1) : void 0;
	const nextMediaUrls = Array.isArray(ctxPayload.MediaUrls) ? ctxPayload.MediaUrls.slice(1) : void 0;
	const nextMediaTypes = Array.isArray(ctxPayload.MediaTypes) ? ctxPayload.MediaTypes.slice(1) : void 0;
	ctxPayload.MediaPaths = nextMediaPaths && nextMediaPaths.length > 0 ? nextMediaPaths : void 0;
	ctxPayload.MediaUrls = nextMediaUrls && nextMediaUrls.length > 0 ? nextMediaUrls : void 0;
	ctxPayload.MediaTypes = nextMediaTypes && nextMediaTypes.length > 0 ? nextMediaTypes : void 0;
	ctxPayload.MediaPath = ctxPayload.MediaPaths?.[0];
	ctxPayload.MediaUrl = ctxPayload.MediaUrls?.[0] ?? ctxPayload.MediaPath;
	ctxPayload.MediaType = ctxPayload.MediaTypes?.[0];
}
//#endregion
//#region extensions/telegram/src/auto-topic-label-config.ts
const AUTO_TOPIC_LABEL_DEFAULT_PROMPT = "Generate a very short topic label (2-4 words, max 25 chars) for a chat conversation based on the user's first message below. No emoji. Use the same language as the message. Be concise and descriptive. Return ONLY the topic name, nothing else.";
function resolveAutoTopicLabelConfig(directConfig, accountConfig) {
	const config = directConfig ?? accountConfig;
	if (config === void 0 || config === true) return {
		enabled: true,
		prompt: AUTO_TOPIC_LABEL_DEFAULT_PROMPT
	};
	if (config === false || config.enabled === false) return null;
	return {
		enabled: true,
		prompt: config.prompt?.trim() || "Generate a very short topic label (2-4 words, max 25 chars) for a chat conversation based on the user's first message below. No emoji. Use the same language as the message. Be concise and descriptive. Return ONLY the topic name, nothing else."
	};
}
//#endregion
//#region extensions/telegram/src/auto-topic-label.ts
async function generateTelegramTopicLabel(params) {
	return await generateConversationLabel({
		...params,
		maxLength: 128
	});
}
//#endregion
//#region extensions/telegram/src/bot/native-quote.ts
const TELEGRAM_NATIVE_QUOTE_MAX_LENGTH = 1024;
function truncateUtf16Safe(value, maxLength) {
	if (value.length <= maxLength) return value;
	let end = Math.max(0, Math.trunc(maxLength));
	const lastCodeUnit = value.charCodeAt(end - 1);
	if (lastCodeUnit >= 55296 && lastCodeUnit <= 56319) end -= 1;
	return value.slice(0, end);
}
function sliceTelegramEntitiesForQuote(entities, quoteLength) {
	if (!entities?.length || quoteLength <= 0) return;
	const sliced = [];
	for (const entity of entities) {
		const offset = Number.isFinite(entity.offset) ? Math.trunc(entity.offset) : 0;
		const length = Number.isFinite(entity.length) ? Math.trunc(entity.length) : 0;
		const start = Math.max(0, offset);
		const end = Math.min(quoteLength, offset + length);
		if (end <= start) continue;
		sliced.push({
			...entity,
			offset: start,
			length: end - start
		});
	}
	return sliced.length > 0 ? sliced : void 0;
}
function buildTelegramNativeQuoteCandidate(params) {
	const source = params.text;
	if (!source?.trim()) return;
	const text = truncateUtf16Safe(source, params.maxLength ?? TELEGRAM_NATIVE_QUOTE_MAX_LENGTH);
	if (!text.trim()) return;
	const candidate = {
		text,
		position: 0
	};
	const entities = sliceTelegramEntitiesForQuote(params.entities, text.length);
	if (entities) candidate.entities = entities;
	return candidate;
}
function addTelegramNativeQuoteCandidate(target, messageId, candidate) {
	if (messageId == null || !candidate) return;
	const key = String(messageId).trim();
	if (!key || target[key]) return;
	target[key] = candidate;
}
//#endregion
//#region extensions/telegram/src/draft-chunking.ts
function resolveTelegramDraftStreamingChunking(cfg, accountId) {
	return resolveChannelDraftStreamingChunking(cfg, "telegram", accountId, { fallbackLimit: TELEGRAM_TEXT_CHUNK_LIMIT });
}
//#endregion
//#region extensions/telegram/src/error-policy.ts
const errorCooldownStore = /* @__PURE__ */ new Map();
const DEFAULT_ERROR_COOLDOWN_MS = 144e5;
function pruneExpiredCooldowns(messageStore, now) {
	for (const [message, expiresAt] of messageStore) if (!isFutureDateTimestampMs(expiresAt, { nowMs: now })) messageStore.delete(message);
}
function resolveTelegramErrorPolicy(params) {
	const configs = [
		params.accountConfig,
		params.groupConfig,
		params.topicConfig
	];
	let policy = "always";
	let cooldownMs = DEFAULT_ERROR_COOLDOWN_MS;
	for (const config of configs) {
		if (config?.errorPolicy) policy = config.errorPolicy;
		if (typeof config?.errorCooldownMs === "number") cooldownMs = config.errorCooldownMs;
	}
	return {
		policy,
		cooldownMs
	};
}
function buildTelegramErrorScopeKey(params) {
	const threadId = params.threadId == null ? "main" : String(params.threadId);
	return `${params.accountId}:${String(params.chatId)}:${threadId}`;
}
function shouldSuppressTelegramError(params) {
	const { scopeKey, cooldownMs, errorMessage } = params;
	const now = asDateTimestampMs(Date.now());
	const messageKey = errorMessage ?? "";
	const scopeStore = errorCooldownStore.get(scopeKey);
	if (now === void 0) {
		errorCooldownStore.delete(scopeKey);
		return false;
	}
	if (scopeStore) {
		pruneExpiredCooldowns(scopeStore, now);
		if (scopeStore.size === 0) errorCooldownStore.delete(scopeKey);
	}
	if (errorCooldownStore.size > 100) for (const [scope, messageStore] of errorCooldownStore) {
		pruneExpiredCooldowns(messageStore, now);
		if (messageStore.size === 0) errorCooldownStore.delete(scope);
	}
	const expiresAt = scopeStore?.get(messageKey);
	if (isFutureDateTimestampMs(expiresAt, { nowMs: now })) return true;
	const nextExpiresAt = resolveExpiresAtMsFromDurationMs(cooldownMs, { nowMs: now });
	if (nextExpiresAt === void 0) {
		scopeStore?.delete(messageKey);
		return false;
	}
	const nextScopeStore = scopeStore ?? /* @__PURE__ */ new Map();
	nextScopeStore.set(messageKey, nextExpiresAt);
	errorCooldownStore.set(scopeKey, nextScopeStore);
	return false;
}
function isSilentErrorPolicy(policy) {
	return policy === "silent";
}
//#endregion
//#region extensions/telegram/src/lane-delivery-text-deliverer.ts
function result(kind, delivery) {
	if (kind === "preview-finalized") {
		const finalized = delivery;
		return {
			kind,
			delivery: {
				...finalized,
				receipt: finalized.receipt ?? createPreviewMessageReceipt({ id: finalized.messageId })
			}
		};
	}
	return { kind };
}
function compactChunks(chunks) {
	const out = [];
	let whitespace = "";
	for (const chunk of chunks) {
		if (!chunk) continue;
		if (chunk.trim().length === 0) {
			whitespace += chunk;
			continue;
		}
		out.push(`${whitespace}${chunk}`);
		whitespace = "";
	}
	if (whitespace && out.length > 0) out[out.length - 1] = `${out[out.length - 1]}${whitespace}`;
	return out;
}
function isDeliveredPrefix(params) {
	if (!params.deliveredText || params.deliveredText.length === 0) return false;
	return params.finalText === params.deliveredText || params.finalText.startsWith(params.deliveredText);
}
function createLaneTextDeliverer(params) {
	const followUpPayload = (payload, text) => params.applyTextToFollowUpPayload ? params.applyTextToFollowUpPayload(payload, text) : params.applyTextToPayload(payload, text);
	const textOnlyPayload = (payload) => {
		const { mediaUrl: _mediaUrl, mediaUrls: _mediaUrls, audioAsVoice: _audioAsVoice, spokenText: _spokenText, ...rest } = payload;
		return rest;
	};
	const mediaChannelData = (channelData, options) => {
		if (!options?.stripButtons) return channelData;
		const telegramData = channelData?.telegram;
		if (!telegramData || typeof telegramData !== "object" || Array.isArray(telegramData)) return channelData;
		const { buttons: _buttons, ...telegramRest } = telegramData;
		if (_buttons === void 0) return channelData;
		const next = { ...channelData };
		if (Object.keys(telegramRest).length > 0) next.telegram = telegramRest;
		else delete next.telegram;
		return Object.keys(next).length > 0 ? next : void 0;
	};
	const withMediaChannelData = (payload, options) => {
		const channelData = mediaChannelData(payload.channelData, options);
		if (channelData === payload.channelData) return payload;
		if (channelData) return {
			...payload,
			channelData
		};
		const { channelData: _channelData, ...rest } = payload;
		return rest;
	};
	const withFallbackTelegramButtons = (payload, buttons) => {
		if (!buttons) return payload;
		const channelData = payload.channelData ?? {};
		const telegramData = channelData.telegram;
		if (telegramData && typeof telegramData === "object" && !Array.isArray(telegramData) && "buttons" in telegramData) return payload;
		const telegramRest = telegramData && typeof telegramData === "object" && !Array.isArray(telegramData) ? telegramData : {};
		return {
			...payload,
			channelData: {
				...channelData,
				telegram: {
					...telegramRest,
					buttons
				}
			}
		};
	};
	const mediaOnlyPayload = (payload, text, options) => {
		if (getReplyPayloadTtsSupplement(payload)) return withFallbackTelegramButtons(withMediaChannelData(buildTtsSupplementMediaPayload(params.applyTextToPayload(payload, text)), options), options?.fallbackButtons);
		if (payload.audioAsVoice === true) {
			const { text: _text, presentation: _presentation, interactive: _interactive, btw: _btw, spokenText: _spokenText, ...voicePayload } = params.applyTextToPayload(payload, text);
			return withFallbackTelegramButtons(withMediaChannelData({
				...voicePayload,
				spokenText: text
			}, options), options?.fallbackButtons);
		}
		const { text: _text, presentation: _presentation, interactive: _interactive, btw: _btw, ...rest } = payload;
		return withFallbackTelegramButtons(withMediaChannelData(rest, options), options?.fallbackButtons);
	};
	const clearUnfinalizedStream = async (lane) => {
		if (!lane.stream || lane.finalized) return;
		await params.clearDraftLane(lane);
		lane.lastPartialText = "";
		lane.hasStreamedMessage = false;
	};
	const discardUnmaterializedStream = async (lane) => {
		const stream = lane.stream;
		if (stream) {
			await stream.discard?.();
			stream.forceNewMessage();
		}
		lane.lastPartialText = "";
		lane.hasStreamedMessage = false;
		lane.finalized = false;
	};
	const rotateFinalizedStream = (lane) => {
		if (!lane.stream || !lane.finalized) return;
		lane.stream.forceNewMessage();
		lane.lastPartialText = "";
		lane.hasStreamedMessage = false;
		lane.finalized = false;
	};
	const streamText = async (laneName, lane, text, payload, useFinalTextRecovery, finalizePreview, buttons) => {
		const stream = lane.stream;
		if (!stream || text.length === 0 || payload.isError) return;
		rotateFinalizedStream(lane);
		const chunks = text.length > params.draftMaxChars ? compactChunks(params.splitFinalTextForStream?.(text) ?? []) : [text];
		const clampActiveChunkIndex = () => Math.min(lane.activeChunkIndex, Math.max(0, chunks.length - 1));
		const activeChunkIndex = clampActiveChunkIndex();
		const activeChunk = chunks[activeChunkIndex];
		const remainingChunks = chunks.slice(activeChunkIndex + 1);
		if (!activeChunk || activeChunk.length > params.draftMaxChars) return;
		const activeFullText = chunks.slice(activeChunkIndex).join("");
		const finalText = activeFullText.trimEnd();
		const deliveredStreamTextBeforeUpdate = stream.lastDeliveredText?.();
		const deliveredPrefixBeforeUpdate = useFinalTextRecovery && deliveredStreamTextBeforeUpdate !== void 0 && isDeliveredPrefix({
			deliveredText: deliveredStreamTextBeforeUpdate,
			finalText
		}) && deliveredStreamTextBeforeUpdate.length > activeChunk.trimEnd().length;
		const finalizeDeliveredPrefix = async (deliveredStreamText, messageId) => {
			lane.finalized = true;
			params.markDelivered();
			let buttonsAttached = false;
			if (buttons) {
				const currentChunk = compactChunks(params.splitFinalTextForStream?.(deliveredStreamText) ?? []).at(-1);
				if (currentChunk && currentChunk.length <= params.draftMaxChars) try {
					await params.editStreamMessage({
						laneName,
						messageId,
						text: currentChunk,
						buttons
					});
					buttonsAttached = true;
				} catch (err) {
					params.log(`telegram: ${laneName} stream button edit failed: ${String(err)}`);
				}
			}
			const suffix = activeFullText.slice(deliveredStreamText.length);
			if (suffix.trim().length > 0) for (const chunk of compactChunks(params.splitFinalTextForStream?.(suffix) ?? [])) {
				if (chunk.trim().length === 0) continue;
				await params.sendPayload(followUpPayload(payload, chunk));
			}
			return result("preview-finalized", {
				content: text,
				promptContextContent: deliveredStreamText,
				messageId,
				buttonsAttached
			});
		};
		const candidateTexts = [stream.lastDeliveredText?.(), lane.lastPartialText];
		if (useFinalTextRecovery && remainingChunks.length === 0 && isPotentialTruncatedFinal(activeFullText)) {
			const resolvedFullCandidate = await params.resolveFinalTextCandidate?.({
				finalText: text,
				laneName
			});
			if (resolvedFullCandidate) {
				const resolvedChunks = resolvedFullCandidate.length > params.draftMaxChars ? compactChunks(params.splitFinalTextForStream?.(resolvedFullCandidate) ?? []) : [resolvedFullCandidate];
				candidateTexts.push(resolvedChunks.slice(activeChunkIndex).join(""));
			}
		}
		const retainedPreview = useFinalTextRecovery && remainingChunks.length === 0 && isPotentialTruncatedFinal(activeFullText) ? selectLongerFinalText({
			finalText: activeFullText,
			candidateTexts
		}) : void 0;
		if (retainedPreview && (!buttons || retainedPreview.length <= params.draftMaxChars)) {
			const previewText = retainedPreview;
			lane.lastPartialText = previewText;
			lane.hasStreamedMessage = true;
			await params.stopDraftLane(lane);
			const messageId = stream.messageId();
			if (typeof messageId !== "number") {
				if (stream.sendMayHaveLanded?.()) {
					lane.finalized = true;
					params.markDelivered();
					return result("preview-retained");
				}
				return;
			}
			const deliveredStreamTextAfterStop = stream.lastDeliveredText?.();
			if (deliveredStreamTextAfterStop !== void 0 && deliveredStreamTextAfterStop !== previewText) return;
			let buttonsAttached = false;
			if (buttons) try {
				await params.editStreamMessage({
					laneName,
					messageId,
					text: previewText,
					buttons
				});
				buttonsAttached = true;
			} catch (err) {
				params.log(`telegram: ${laneName} stream button edit failed: ${String(err)}`);
			}
			for (const chunk of remainingChunks) {
				if (chunk.trim().length === 0) continue;
				await params.sendPayload(followUpPayload(payload, chunk));
			}
			lane.finalized = true;
			params.markDelivered();
			return result("preview-finalized", {
				content: previewText,
				promptContextContent: previewText,
				messageId,
				buttonsAttached
			});
		}
		if (!deliveredPrefixBeforeUpdate) {
			lane.lastPartialText = activeChunk;
			lane.hasStreamedMessage = true;
			lane.finalized = false;
			stream.update(activeChunk);
		}
		if (finalizePreview) await params.stopDraftLane(lane);
		else await params.flushDraftLane(lane);
		const activeChunkIndexAfterStop = useFinalTextRecovery ? clampActiveChunkIndex() : activeChunkIndex;
		const activeChunkAfterStop = chunks[activeChunkIndexAfterStop] ?? activeChunk;
		const remainingChunksAfterStop = chunks.slice(activeChunkIndexAfterStop + 1);
		const messageId = stream.messageId();
		if (typeof messageId !== "number") {
			if (finalizePreview && stream.sendMayHaveLanded?.()) {
				lane.finalized = true;
				params.markDelivered();
				return result("preview-retained");
			}
			if (!finalizePreview) await discardUnmaterializedStream(lane);
			return;
		}
		const deliveredStreamTextAfterStop = stream.lastDeliveredText?.();
		const activeChunkTextAfterStop = activeChunkAfterStop.trimEnd();
		const retainedActiveChunkAfterStop = activeChunkIndexAfterStop !== activeChunkIndex && deliveredStreamTextAfterStop === activeChunk.trimEnd();
		if (finalizePreview && deliveredStreamTextAfterStop !== void 0 && deliveredStreamTextAfterStop !== activeChunkTextAfterStop && !retainedActiveChunkAfterStop) {
			if (useFinalTextRecovery && isDeliveredPrefix({
				deliveredText: deliveredStreamTextAfterStop,
				finalText
			}) && deliveredStreamTextAfterStop.length > activeChunkTextAfterStop.length) return await finalizeDeliveredPrefix(deliveredStreamTextAfterStop, messageId);
			return;
		}
		if (deliveredPrefixBeforeUpdate && deliveredStreamTextAfterStop === void 0) return await finalizeDeliveredPrefix(deliveredStreamTextBeforeUpdate, messageId);
		params.markDelivered();
		let buttonsAttached = false;
		if (buttons) try {
			await params.editStreamMessage({
				laneName,
				messageId,
				text: activeChunkAfterStop,
				buttons
			});
			buttonsAttached = true;
		} catch (err) {
			params.log(`telegram: ${laneName} stream button edit failed: ${String(err)}`);
		}
		if (finalizePreview) {
			lane.finalized = true;
			for (const chunk of remainingChunksAfterStop) {
				if (chunk.trim().length === 0) continue;
				await params.sendPayload(followUpPayload(payload, chunk));
			}
			return result("preview-finalized", {
				content: text,
				promptContextContent: activeChunkAfterStop,
				messageId,
				buttonsAttached
			});
		}
		return result("preview-updated");
	};
	return async ({ laneName, text, payload, infoKind, buttons, finalizePreview: requestedFinalizePreview, durable: requestedDurable }) => {
		const lane = params.lanes[laneName];
		const reply = resolveSendableOutboundReplyParts(payload, { text });
		const isDurableFinal = infoKind === "final";
		const finalizePreview = requestedFinalizePreview ?? isDurableFinal;
		const durable = requestedDurable ?? isDurableFinal;
		const streamed = !reply.hasMedia ? await streamText(laneName, lane, text, payload, isDurableFinal, finalizePreview, buttons) : void 0;
		if (streamed) return streamed;
		if (finalizePreview && reply.hasMedia && lane.stream && lane.hasStreamedMessage && !lane.finalized && text.trim().length > 0) {
			const finalizedPreview = await streamText(laneName, lane, text, textOnlyPayload(payload), isDurableFinal, true, buttons);
			if (finalizedPreview) {
				const stripButtons = finalizedPreview.kind === "preview-finalized" && finalizedPreview.delivery.buttonsAttached === true;
				const mediaText = finalizedPreview.kind === "preview-finalized" ? finalizedPreview.delivery.content : text;
				await params.sendPayload(mediaOnlyPayload(payload, mediaText, {
					stripButtons,
					fallbackButtons: stripButtons ? void 0 : buttons
				}), { durable });
				return finalizedPreview;
			}
		}
		if (finalizePreview) await clearUnfinalizedStream(lane);
		const delivered = await params.sendPayload(params.applyTextToPayload(payload, text), { durable });
		if (delivered && finalizePreview) lane.finalized = true;
		return delivered ? result("sent") : result("skipped");
	};
}
//#endregion
//#region extensions/telegram/src/lane-delivery-state.ts
function createLaneDeliveryStateTracker() {
	const state = {
		delivered: false,
		skippedNonSilent: 0,
		failedNonSilent: 0
	};
	return {
		markDelivered: () => {
			state.delivered = true;
		},
		markNonSilentSkip: () => {
			state.skippedNonSilent += 1;
		},
		markNonSilentFailure: () => {
			state.failedNonSilent += 1;
		},
		snapshot: () => ({ ...state })
	};
}
//#endregion
//#region extensions/telegram/src/reasoning-lane-coordinator.ts
const REASONING_MESSAGE_RE = /^Thinking\.{0,3}\s*_/u;
const LEGACY_REASONING_MESSAGE_PREFIX = "Reasoning:\n";
const REASONING_TAG_PREFIXES = [
	"<think",
	"<thinking",
	"<thought",
	"<antthinking",
	"</think",
	"</thinking",
	"</thought",
	"</antthinking"
];
const THINKING_TAG_RE = /<\s*(\/?)\s*(?:think(?:ing)?|thought|antthinking)\b[^<>]*>/gi;
function extractThinkingFromTaggedStreamOutsideCode(text) {
	if (!text) return "";
	const codeRegions = findCodeRegions(text);
	let result = "";
	let lastIndex = 0;
	let inThinking = false;
	THINKING_TAG_RE.lastIndex = 0;
	for (const match of text.matchAll(THINKING_TAG_RE)) {
		const idx = match.index ?? 0;
		if (isInsideCode(idx, codeRegions)) continue;
		if (inThinking) result += text.slice(lastIndex, idx);
		inThinking = !(match[1] === "/");
		lastIndex = idx + match[0].length;
	}
	if (inThinking) result += text.slice(lastIndex);
	return result.trim();
}
function isPartialReasoningTagPrefix(text) {
	const trimmed = normalizeLowercaseStringOrEmpty(text.trimStart());
	if (!trimmed.startsWith("<")) return false;
	if (trimmed.includes(">")) return false;
	return REASONING_TAG_PREFIXES.some((prefix) => prefix.startsWith(trimmed));
}
function splitTelegramReasoningText(text, isReasoning) {
	if (typeof text !== "string") return {};
	const trimmed = text.trim();
	if (isPartialReasoningTagPrefix(trimmed)) return {};
	if (REASONING_MESSAGE_RE.test(trimmed)) return { reasoningText: trimmed };
	if (trimmed.startsWith(LEGACY_REASONING_MESSAGE_PREFIX) && trimmed.length > 11) return { reasoningText: trimmed };
	const taggedReasoning = extractThinkingFromTaggedStreamOutsideCode(text);
	const strippedAnswer = stripReasoningTagsFromText(text, {
		mode: "strict",
		trim: "both"
	});
	if (isReasoning === true) return { reasoningText: formatReasoningMessage(taggedReasoning || strippedAnswer || text) };
	if (!taggedReasoning && strippedAnswer === text) return { answerText: text };
	return {
		reasoningText: taggedReasoning ? formatReasoningMessage(taggedReasoning) : void 0,
		answerText: strippedAnswer || void 0
	};
}
function createTelegramReasoningStepState() {
	let reasoningStatus = "none";
	let bufferedFinalAnswer;
	const noteReasoningHint = () => {
		if (reasoningStatus === "none") reasoningStatus = "hinted";
	};
	const noteReasoningDelivered = () => {
		reasoningStatus = "delivered";
	};
	const shouldBufferFinalAnswer = () => {
		return reasoningStatus === "hinted" && !bufferedFinalAnswer;
	};
	const bufferFinalAnswer = (value) => {
		bufferedFinalAnswer = value;
	};
	const takeBufferedFinalAnswer = (currentGeneration) => {
		if (currentGeneration !== void 0 && bufferedFinalAnswer?.bufferedGeneration !== void 0 && bufferedFinalAnswer.bufferedGeneration !== currentGeneration) return;
		const value = bufferedFinalAnswer;
		bufferedFinalAnswer = void 0;
		return value;
	};
	const resetForNextStep = () => {
		reasoningStatus = "none";
		bufferedFinalAnswer = void 0;
	};
	return {
		noteReasoningHint,
		noteReasoningDelivered,
		shouldBufferFinalAnswer,
		bufferFinalAnswer,
		takeBufferedFinalAnswer,
		resetForNextStep
	};
}
//#endregion
//#region extensions/telegram/src/sequential-key.ts
const TELEGRAM_READ_ONLY_STATUS_COMMAND_KEYS = new Set([
	"commands",
	"context",
	"help",
	"status",
	"tasks",
	"tools",
	"whoami"
]);
function isTelegramReadOnlyControlLaneText(params) {
	const alias = maybeResolveTextAlias(normalizeCommandBody(params.rawText?.trim() ?? "", params.botUsername ? { botUsername: params.botUsername } : void 0));
	if (!alias) return false;
	const command = listChatCommands().find((entry) => entry.textAliases.some((candidate) => candidate.trim().toLowerCase() === alias));
	return command?.category === "status" && TELEGRAM_READ_ONLY_STATUS_COMMAND_KEYS.has(command.key);
}
function isTelegramTargetedStopCommand(rawText, botUsername) {
	const trimmed = rawText?.trim();
	if (!trimmed) return false;
	const match = trimmed.match(/^\/stop@([A-Za-z0-9_]+)(?:$|\s|[.!?…,,。;;::'"’”)\]}])/iu);
	if (!match) return false;
	const normalizedBotUsername = botUsername?.trim().toLowerCase();
	if (!normalizedBotUsername) return true;
	return match[1]?.toLowerCase() === normalizedBotUsername;
}
function isTelegramControlLaneText(params) {
	if (isAbortRequestText(params.rawText, params.botUsername ? { botUsername: params.botUsername } : void 0)) return true;
	if (isTelegramTargetedStopCommand(params.rawText, params.botUsername)) return true;
	return isTelegramReadOnlyControlLaneText(params);
}
function getTelegramSequentialKey(ctx) {
	const reaction = ctx.update?.message_reaction;
	if (reaction?.chat?.id) return `telegram:${reaction.chat.id}`;
	const msg = ctx.message ?? ctx.channelPost ?? ctx.editedMessage ?? ctx.editedChannelPost ?? ctx.update?.message ?? ctx.update?.edited_message ?? ctx.update?.channel_post ?? ctx.update?.edited_channel_post ?? ctx.update?.callback_query?.message;
	const chatId = msg?.chat?.id ?? ctx.chat?.id;
	const rawText = msg?.text ?? msg?.caption;
	const botUsername = ctx.me?.username;
	if (isTelegramControlLaneText({
		rawText,
		botUsername
	})) {
		if (typeof chatId === "number") return `telegram:${chatId}:control`;
		return "telegram:control";
	}
	if (isBtwRequestText(rawText, botUsername ? { botUsername } : void 0)) {
		const messageId = msg?.message_id;
		if (typeof chatId === "number" && typeof messageId === "number") return `telegram:${chatId}:btw:${messageId}`;
		if (typeof chatId === "number") return `telegram:${chatId}:btw`;
		return "telegram:btw";
	}
	const callbackData = ctx.update?.callback_query?.data;
	if (callbackData && parseExecApprovalCommandText(callbackData) !== null) {
		if (typeof chatId === "number") return `telegram:${chatId}:approval`;
		return "telegram:approval";
	}
	const isGroup = msg?.chat?.type === "group" || msg?.chat?.type === "supergroup";
	const messageThreadId = msg?.message_thread_id;
	const isForum = resolveTelegramMessageForumFlagHint({
		chatType: msg?.chat?.type,
		isForum: msg?.chat?.is_forum,
		isTopicMessage: msg?.is_topic_message
	});
	const threadId = isGroup ? resolveTelegramForumThreadId({
		isForum,
		messageThreadId
	}) : messageThreadId;
	if (typeof chatId === "number") return threadId != null ? `telegram:${chatId}:topic:${threadId}` : `telegram:${chatId}`;
	return "telegram:unknown";
}
//#endregion
//#region extensions/telegram/src/telegram-reply-fence.ts
const telegramReplyFenceByKey = /* @__PURE__ */ new Map();
const telegramReplyFenceKeysByLane = /* @__PURE__ */ new Map();
function buildTelegramReplyFenceLaneKey(params) {
	return `${params.accountId}\0${params.sequentialKey}`;
}
function buildTelegramNonInterruptingReplyFenceKey(params) {
	return `${buildTelegramNonInterruptingReplyFenceKeyPrefix(params.activeKey)}${params.laneKey}`;
}
function buildTelegramNonInterruptingReplyFenceKeyPrefix(activeKey) {
	return `${activeKey}\0non-interrupting\0`;
}
function normalizeTelegramFenceKey(value) {
	if (typeof value !== "string") return;
	const trimmed = value.trim();
	return trimmed.length > 0 ? trimmed : void 0;
}
function resolveTelegramReplyFenceKey(params) {
	const baseKey = normalizeTelegramFenceKey(params.ctxPayload.CommandTargetSessionKey) ?? normalizeTelegramFenceKey(params.ctxPayload.SessionKey) ?? `telegram:${String(params.chatId)}:${params.threadSpec.scope ?? "default"}:${params.threadSpec.id ?? "root"}`;
	const roomEventKey = `${baseKey}:room_event`;
	return {
		activeKey: params.ctxPayload.InboundEventKind === "room_event" ? roomEventKey : baseKey,
		roomEventKey
	};
}
function abortTelegramReplyFenceControllers(state) {
	for (const controller of state.abortControllers ?? []) controller.abort();
	state.abortControllers?.clear();
}
function deleteTelegramReplyFenceState(key, state) {
	telegramReplyFenceByKey.delete(key);
	for (const laneKey of state.laneKeys ?? []) {
		const keys = telegramReplyFenceKeysByLane.get(laneKey);
		keys?.delete(key);
		if (keys?.size === 0) telegramReplyFenceKeysByLane.delete(laneKey);
	}
}
function maybeDeleteTelegramReplyFenceState(key, state) {
	if (state.activeDispatches <= 0 && (state.abortControllers?.size ?? 0) === 0) deleteTelegramReplyFenceState(key, state);
	else telegramReplyFenceByKey.set(key, state);
}
function beginTelegramReplyFence(params) {
	const state = telegramReplyFenceByKey.get(params.key) ?? {
		generation: 0,
		activeDispatches: 0
	};
	if (params.supersede) {
		state.generation += 1;
		abortTelegramReplyFenceControllers(state);
		supersedeTelegramNonInterruptingReplyFenceChildren(params.key);
	}
	if (params.abortController) (state.abortControllers ??= /* @__PURE__ */ new Set()).add(params.abortController);
	const laneKey = normalizeTelegramFenceKey(params.laneKey);
	if (laneKey) {
		(state.laneKeys ??= /* @__PURE__ */ new Set()).add(laneKey);
		const keys = telegramReplyFenceKeysByLane.get(laneKey) ?? /* @__PURE__ */ new Set();
		keys.add(params.key);
		telegramReplyFenceKeysByLane.set(laneKey, keys);
	}
	state.activeDispatches += 1;
	telegramReplyFenceByKey.set(params.key, state);
	return state.generation;
}
function supersedeTelegramReplyFenceState(key) {
	const state = telegramReplyFenceByKey.get(key);
	if (!state) return false;
	state.generation += 1;
	abortTelegramReplyFenceControllers(state);
	maybeDeleteTelegramReplyFenceState(key, state);
	return true;
}
function supersedeTelegramNonInterruptingReplyFenceChildren(key) {
	let superseded = false;
	const childPrefix = buildTelegramNonInterruptingReplyFenceKeyPrefix(key);
	for (const childKey of telegramReplyFenceByKey.keys()) if (childKey.startsWith(childPrefix)) superseded = supersedeTelegramReplyFenceState(childKey) || superseded;
	return superseded;
}
function supersedeTelegramReplyFence(key) {
	let superseded = supersedeTelegramReplyFenceState(key);
	superseded = supersedeTelegramNonInterruptingReplyFenceChildren(key) || superseded;
	return superseded;
}
function supersedeTelegramReplyFenceLane(laneKey) {
	const keys = [...telegramReplyFenceKeysByLane.get(laneKey) ?? []];
	let superseded = false;
	for (const key of keys) superseded = supersedeTelegramReplyFence(key) || superseded;
	return superseded;
}
function isTelegramReplyFenceSuperseded(params) {
	return (telegramReplyFenceByKey.get(params.key)?.generation ?? 0) !== params.generation;
}
function endTelegramReplyFence(key, abortController) {
	const state = telegramReplyFenceByKey.get(key);
	if (!state) return;
	if (abortController) state.abortControllers?.delete(abortController);
	state.activeDispatches = Math.max(0, state.activeDispatches - 1);
	maybeDeleteTelegramReplyFenceState(key, state);
}
function releaseTelegramReplyFenceAbortController(key, abortController) {
	if (!abortController) return;
	const state = telegramReplyFenceByKey.get(key);
	if (!state) return;
	state.abortControllers?.delete(abortController);
	maybeDeleteTelegramReplyFenceState(key, state);
}
function isRecognizedTelegramTextCommand(rawText) {
	return maybeResolveTextAlias(normalizeCommandBody(rawText)) != null;
}
function shouldSupersedeTelegramReplyFence(ctxPayload) {
	const dispatchText = ctxPayload.CommandBody ?? ctxPayload.RawBody ?? ctxPayload.Body ?? "";
	if (isAbortRequestText(dispatchText)) return ctxPayload.CommandAuthorized;
	if (isBtwRequestText(dispatchText) || isTelegramReadOnlyControlLaneText({ rawText: dispatchText })) return false;
	if (ctxPayload.ChatType === "direct") {
		if (ctxPayload.CommandAuthorized && (isExplicitCommandTurn(ctxPayload.CommandTurn) || isRecognizedTelegramTextCommand(dispatchText))) return true;
		return false;
	}
	return true;
}
//#endregion
//#region extensions/telegram/src/bot-message-dispatch.ts
const EMPTY_RESPONSE_FALLBACK = "No response generated. Please try again.";
const silentReplyDispatchLogger = createSubsystemLogger("telegram/silent-reply-dispatch");
/** Minimum chars before sending first streaming message (improves push notification UX) */
const DRAFT_MIN_INITIAL_CHARS = 30;
function resolveDraftPartialText(previous, update) {
	const nextText = update.replace || update.isReasoningSnapshot || update.delta === void 0 ? update.text : `${previous}${update.delta}`;
	if (nextText === previous) return;
	return nextText;
}
function resolvePayloadTelegramInlineButtons(payload) {
	const telegramData = payload.channelData?.telegram;
	const presentation = normalizeMessagePresentation(payload.presentation);
	return resolveTelegramInlineButtons({
		buttons: telegramData?.buttons,
		presentation,
		interactive: payload.interactive
	});
}
function hasExecApprovalPayload(payload) {
	const channelData = payload.channelData;
	if (!channelData || typeof channelData !== "object" || Array.isArray(channelData)) return false;
	const execApproval = channelData.execApproval;
	return Boolean(execApproval && typeof execApproval === "object" && !Array.isArray(execApproval));
}
function canUseNativeToolProgressDraft(params) {
	return !params.reply.hasMedia && params.payload.isError !== true && !hasExecApprovalPayload(params.payload) && params.buttons === void 0;
}
function canUseNativeToolProgressDraftForChat(params) {
	if (!resolveChannelStreamingPreviewNativeToolProgress(params.telegramCfg)) return false;
	const allowFrom = resolveChannelStreamingPreviewNativeToolProgressAllowFrom(params.telegramCfg);
	if (!allowFrom || allowFrom.length === 0) return true;
	const normalized = normalizeAllowFrom(allowFrom);
	return normalized.hasWildcard || normalized.entries.includes(String(params.chatId));
}
async function resolveStickerVisionSupport(cfg, agentId) {
	try {
		const catalog = await loadModelCatalog({ config: cfg });
		const defaultModel = resolveDefaultModelForAgent({
			cfg,
			agentId
		});
		const entry = findModelInCatalog(catalog, defaultModel.provider, defaultModel.model);
		if (!entry) return false;
		return modelSupportsVision(entry);
	} catch {
		return false;
	}
}
function createFreshTelegramSessionStoreLoader(params) {
	const storesByPath = /* @__PURE__ */ new Map();
	const load = ((agentId) => {
		const storePath = params.telegramDeps.resolveStorePath(params.cfg.session?.store, { agentId });
		const cachedStore = storesByPath.get(storePath);
		if (cachedStore) return {
			storePath,
			store: cachedStore
		};
		const store = (params.telegramDeps.loadSessionStore ?? loadSessionStore)(storePath, { skipCache: true });
		storesByPath.set(storePath, store);
		return {
			storePath,
			store
		};
	});
	load.clear = () => storesByPath.clear();
	return load;
}
function resolveTelegramReasoningLevel(params) {
	const { cfg, sessionKey, agentId } = params;
	const configDefault = resolveTelegramConfigReasoningDefault(cfg, agentId);
	if (!sessionKey) return configDefault;
	try {
		const { store } = params.loadFreshSessionStore(agentId);
		const level = resolveSessionStoreEntry({
			store,
			sessionKey
		}).existing?.reasoningLevel;
		if (level === "on" || level === "stream" || level === "off") return level;
	} catch {
		return "off";
	}
	return configDefault;
}
function resolveTelegramMirroredTranscriptText(payload) {
	const mediaUrls = payload.mediaUrls?.filter((url) => url.trim()) ?? [];
	if (mediaUrls.length > 0) return mediaUrls.map((url) => {
		const pathname = url.split("#")[0]?.split("?")[0] ?? url;
		const base = path.basename(pathname);
		return base && base !== "." && base !== "/" ? base : "media";
	}).join(", ");
	const text = payload.text?.trim();
	return text ? text : null;
}
async function mirrorTelegramAssistantReplyToTranscript(params) {
	const text = resolveTelegramMirroredTranscriptText(params.payload);
	if (!text) return;
	const { storePath, store } = params.loadFreshSessionStore(params.route.agentId);
	const sessionEntry = resolveSessionStoreEntry({
		store,
		sessionKey: params.sessionKey
	}).existing;
	if (!sessionEntry?.sessionId) return;
	const { sessionFile } = await resolveAndPersistSessionFile({
		sessionId: sessionEntry.sessionId,
		sessionKey: params.sessionKey,
		sessionStore: store,
		storePath,
		sessionEntry,
		agentId: params.route.agentId,
		sessionsDir: path.dirname(storePath)
	});
	const { appended, messageId, message: appendedMessage } = await appendSessionTranscriptMessage({
		transcriptPath: sessionFile,
		message: {
			role: "assistant",
			content: [{
				type: "text",
				text
			}],
			api: "openai-responses",
			provider: "openclaw",
			model: "delivery-mirror",
			usage: {
				input: 0,
				output: 0,
				total: 0,
				prompt_tokens: 0,
				completion_tokens: 0,
				total_tokens: 0,
				cache: {
					read: 0,
					write: 0,
					cacheRead: 0,
					cacheWrite: 0,
					total: 0
				}
			},
			stopReason: "stop",
			timestamp: Date.now()
		},
		config: params.cfg
	});
	if (appended) {
		const transcriptMarkerUpdatedAt = Date.now();
		await updateSessionStoreEntry({
			storePath,
			sessionKey: params.sessionKey,
			update: (current) => current.sessionId === sessionEntry.sessionId ? { updatedAt: transcriptMarkerUpdatedAt } : null
		});
	}
	emitSessionTranscriptUpdate({
		sessionFile,
		sessionKey: params.sessionKey,
		agentId: params.route.agentId,
		message: appendedMessage,
		messageId
	});
}
const MAX_PROGRESS_MARKDOWN_TEXT_CHARS = 300;
const TELEGRAM_GENERAL_TOPIC_ID = 1;
function clipProgressMarkdownText(text) {
	if (text.length <= MAX_PROGRESS_MARKDOWN_TEXT_CHARS) return text;
	return `${text.slice(0, MAX_PROGRESS_MARKDOWN_TEXT_CHARS - 1).trimEnd()}…`;
}
function sanitizeProgressMarkdownText(text) {
	return text.replaceAll("`", "'");
}
function formatProgressAsMarkdownCode(text) {
	return `\`${sanitizeProgressMarkdownText(clipProgressMarkdownText(text))}\``;
}
function formatTelegramProgressLine(text) {
	const trimmed = text.trim();
	return trimmed.startsWith("_") && trimmed.endsWith("_") ? trimmed : formatProgressAsMarkdownCode(text);
}
function normalizeTelegramThreadId(value) {
	return parseStrictPositiveInteger(value);
}
function resolveTelegramForumThreadScopeFromSessionKey(sessionKey) {
	if (typeof sessionKey !== "string") return;
	const match = /:telegram:group:(-?\d+):topic:(\d+)(?::|$)/.exec(sessionKey);
	const threadId = normalizeTelegramThreadId(match?.[2]);
	if (!match?.[1] || threadId == null) return;
	return {
		chatId: match[1],
		threadId
	};
}
function resolveDispatchTelegramThreadSpec(params) {
	if (params.threadSpec.scope !== "forum" || params.threadSpec.id != null && params.threadSpec.id !== TELEGRAM_GENERAL_TOPIC_ID) return params.threadSpec;
	const scopedThread = resolveTelegramForumThreadScopeFromSessionKey(params.ctxPayload.SessionKey);
	const scopedThreadId = scopedThread?.chatId === String(params.chatId) ? scopedThread.threadId : void 0;
	const payloadThreadId = normalizeTelegramThreadId(params.ctxPayload.MessageThreadId) ?? normalizeTelegramThreadId(params.ctxPayload.TransportThreadId);
	const recoveredThreadId = scopedThreadId ?? payloadThreadId;
	return recoveredThreadId == null || recoveredThreadId === params.threadSpec.id ? params.threadSpec : {
		...params.threadSpec,
		id: recoveredThreadId
	};
}
function normalizeDispatchTelegramThreadPayload(params) {
	if (params.threadSpec.scope !== "forum" || params.threadSpec.id == null) return params.context;
	const messageThreadId = normalizeTelegramThreadId(params.context.ctxPayload.MessageThreadId);
	const transportThreadId = normalizeTelegramThreadId(params.context.ctxPayload.TransportThreadId);
	if (messageThreadId === params.threadSpec.id && transportThreadId === params.threadSpec.id) return params.context;
	return {
		...params.context,
		ctxPayload: {
			...params.context.ctxPayload,
			MessageThreadId: params.threadSpec.id,
			TransportThreadId: params.threadSpec.id
		}
	};
}
function extractCurrentTelegramBody(body) {
	if (!body) return "";
	const markerIndex = body.lastIndexOf(CURRENT_MESSAGE_MARKER);
	if (markerIndex === -1) return body;
	return body.slice(markerIndex + CURRENT_MESSAGE_MARKER.length).trimStart();
}
function buildRecoveredTelegramBody(params) {
	if (!params.context.isGroup || !params.historyKey || params.context.historyLimit <= 0) return params.currentMessage;
	const groupLabel = buildGroupLabel(params.context.msg, params.context.chatId, params.threadSpec.id);
	const envelopeOptions = resolveEnvelopeFormatOptions(params.cfg);
	return createChannelHistoryWindow({ historyMap: params.context.groupHistories }).buildPendingContext({
		historyKey: params.historyKey,
		limit: params.context.historyLimit,
		currentMessage: params.currentMessage,
		formatEntry: (entry) => formatInboundEnvelope({
			channel: "Telegram",
			from: groupLabel,
			timestamp: entry.timestamp,
			body: `${entry.body} [id:${entry.messageId ?? "unknown"} chat:${params.context.chatId}]`,
			chatType: "group",
			senderLabel: entry.sender,
			envelope: envelopeOptions
		})
	});
}
function buildRecoveredTelegramChatActionSender(params) {
	return async () => {
		try {
			await withTelegramApiErrorLogging({
				operation: "sendChatAction",
				fn: () => params.context.sendChatActionHandler.sendChatAction(params.context.chatId, params.action, buildTypingThreadParams(params.threadId))
			});
		} catch (err) {
			if (params.action !== "record_voice") throw err;
			logVerbose(`telegram record_voice cue failed for chat ${params.context.chatId}: ${String(err)}`);
		}
	};
}
function migrateRecoveredTelegramRoomEventHistory(params) {
	const originalHistoryKey = params.context.historyKey;
	const recoveredHistoryKey = params.recoveredHistoryKey;
	if (!params.context.isGroup || params.context.ctxPayload.InboundEventKind !== "room_event" || !originalHistoryKey || !recoveredHistoryKey || originalHistoryKey === recoveredHistoryKey || params.context.historyLimit <= 0) return;
	const originalEntries = params.context.groupHistories.get(originalHistoryKey);
	if (!originalEntries?.length) return;
	const messageId = params.context.ctxPayload.MessageSid;
	const rawBody = params.context.ctxPayload.RawBody;
	const entryIndex = originalEntries.findLastIndex((entry) => {
		if (messageId && entry.messageId === messageId) return true;
		return !messageId && typeof rawBody === "string" && entry.body === rawBody;
	});
	if (entryIndex === -1) return;
	const [entry] = originalEntries.splice(entryIndex, 1);
	if (!entry) return;
	createChannelHistoryWindow({ historyMap: params.context.groupHistories }).record({
		historyKey: recoveredHistoryKey,
		limit: params.context.historyLimit,
		entry
	});
}
function resolveDispatchTelegramContext(params) {
	const threadSpec = resolveDispatchTelegramThreadSpec({
		chatId: params.context.chatId,
		ctxPayload: params.context.ctxPayload,
		threadSpec: params.context.threadSpec
	});
	if (threadSpec === params.context.threadSpec || threadSpec.scope !== "forum") return normalizeDispatchTelegramThreadPayload({
		context: params.context,
		threadSpec
	});
	const recoveredRoutingTarget = buildTelegramInboundOriginTarget(params.context.chatId, threadSpec);
	const recoveredFrom = params.context.isGroup ? buildTelegramGroupFrom(params.context.chatId, threadSpec.id) : params.context.ctxPayload.From;
	const recoveredUpdateLastRoute = params.context.turn.record.updateLastRoute && threadSpec.id != null ? {
		...params.context.turn.record.updateLastRoute,
		to: `telegram:${params.context.chatId}:topic:${threadSpec.id}`,
		threadId: String(threadSpec.id)
	} : params.context.turn.record.updateLastRoute;
	const recoveredHistoryKey = params.context.isGroup ? buildTelegramGroupPeerId(params.context.chatId, threadSpec.id) : params.context.historyKey;
	migrateRecoveredTelegramRoomEventHistory({
		context: params.context,
		recoveredHistoryKey
	});
	const recoveredInboundHistory = params.context.isGroup && recoveredHistoryKey && params.context.historyLimit > 0 ? createChannelHistoryWindow({ historyMap: params.context.groupHistories }).buildInboundHistory({
		historyKey: recoveredHistoryKey,
		limit: params.context.historyLimit
	}) : params.context.ctxPayload.InboundHistory;
	const recoveredBodyForAgent = extractCurrentTelegramBody(params.context.ctxPayload.BodyForAgent ?? params.context.ctxPayload.Body);
	const recoveredBody = buildRecoveredTelegramBody({
		cfg: params.cfg,
		context: params.context,
		currentMessage: recoveredBodyForAgent,
		historyKey: recoveredHistoryKey,
		threadSpec
	});
	const recoveredSendTyping = buildRecoveredTelegramChatActionSender({
		context: params.context,
		threadId: threadSpec.id,
		action: "typing"
	});
	const recoveredSendRecordVoice = buildRecoveredTelegramChatActionSender({
		context: params.context,
		threadId: threadSpec.id,
		action: "record_voice"
	});
	return {
		...params.context,
		historyKey: recoveredHistoryKey,
		threadSpec,
		resolvedThreadId: threadSpec.id,
		replyThreadId: threadSpec.id,
		sendTyping: recoveredSendTyping,
		sendRecordVoice: recoveredSendRecordVoice,
		turn: {
			...params.context.turn,
			record: {
				...params.context.turn.record,
				updateLastRoute: recoveredUpdateLastRoute
			}
		},
		ctxPayload: threadSpec.id == null ? params.context.ctxPayload : {
			...params.context.ctxPayload,
			Body: recoveredBody,
			BodyForAgent: recoveredBodyForAgent,
			From: recoveredFrom,
			InboundHistory: recoveredInboundHistory,
			MessageThreadId: threadSpec.id,
			OriginatingTo: recoveredRoutingTarget,
			To: recoveredRoutingTarget,
			TransportThreadId: threadSpec.id
		}
	};
}
const dispatchTelegramMessage = async ({ context, bot, cfg, runtime, replyToMode, streamMode, textLimit, telegramCfg, telegramDeps: injectedTelegramDeps, opts }) => {
	const dispatchStartedAt = Date.now();
	const dispatchContext = resolveDispatchTelegramContext({
		cfg,
		context
	});
	const telegramDeps = injectedTelegramDeps ?? (await import("./bot-deps-DmZLNWJj.js")).defaultTelegramBotDeps;
	const loadFreshSessionStore = createFreshTelegramSessionStoreLoader({
		cfg,
		telegramDeps
	});
	const { ctxPayload, msg, chatId, isGroup, groupConfig, topicConfig, threadSpec, historyKey, historyLimit, groupHistories, route, skillFilter, sendTyping, sendRecordVoice, ackReactionPromise, reactionApi, removeAckAfterReply, statusReactionController: rawStatusReactionController } = dispatchContext;
	const isRoomEvent = ctxPayload.InboundEventKind === "room_event";
	const statusReactionController = isRoomEvent ? null : rawStatusReactionController;
	const statusReactionTiming = {
		...DEFAULT_TIMING,
		...cfg.messages?.statusReactions?.timing
	};
	const clearTelegramStatusReaction = async () => {
		if (!msg.message_id || !reactionApi) return;
		await reactionApi(chatId, msg.message_id, []);
	};
	const finalizeTelegramStatusReaction = async (params) => {
		if (!statusReactionController) return;
		if (params.outcome === "done") {
			await statusReactionController.setDone();
			if (removeAckAfterReply) {
				await sleepWithAbort(statusReactionTiming.doneHoldMs);
				await clearTelegramStatusReaction();
			} else await statusReactionController.restoreInitial();
			return;
		}
		await statusReactionController.setError();
		if (params.hasFinalResponse) {
			if (removeAckAfterReply) {
				await sleepWithAbort(statusReactionTiming.errorHoldMs);
				await clearTelegramStatusReaction();
			} else await statusReactionController.restoreInitial();
			return;
		}
		if (removeAckAfterReply) await sleepWithAbort(statusReactionTiming.errorHoldMs);
		await statusReactionController.restoreInitial();
	};
	const replyFenceKey = resolveTelegramReplyFenceKey({
		ctxPayload,
		chatId,
		threadSpec
	});
	const replyFenceLaneKey = getTelegramSequentialKey({
		message: msg,
		...context.primaryCtx.me ? { me: context.primaryCtx.me } : {}
	});
	const scopedReplyFenceLaneKey = buildTelegramReplyFenceLaneKey({
		accountId: route.accountId,
		sequentialKey: replyFenceLaneKey
	});
	let activeReplyFenceKey = replyFenceKey.activeKey;
	let replyFenceGeneration;
	const replyAbortController = new AbortController();
	let replyAbortControllerQueued = false;
	let dispatchWasSuperseded;
	const isDispatchSuperseded = () => replyFenceGeneration !== void 0 && isTelegramReplyFenceSuperseded({
		key: activeReplyFenceKey,
		generation: replyFenceGeneration
	});
	const releaseReplyFence = () => {
		if (replyFenceGeneration === void 0) return;
		endTelegramReplyFence(activeReplyFenceKey, replyAbortControllerQueued ? void 0 : replyAbortController);
		replyFenceGeneration = void 0;
	};
	const draftMaxChars = streamMode === "block" ? Math.min(resolveTelegramDraftStreamingChunking(cfg, route.accountId).maxChars, 4096) : Math.min(textLimit, 4096);
	const tableMode = resolveMarkdownTableMode({
		cfg,
		channel: "telegram",
		accountId: route.accountId
	});
	const renderStreamText = (text) => ({
		text: renderTelegramHtmlText(text, { tableMode }),
		parseMode: "HTML"
	});
	const accountBlockStreamingEnabled = resolveChannelStreamingBlockEnabled(telegramCfg) ?? cfg.agents?.defaults?.blockStreamingDefault === "on";
	const resolvedReasoningLevel = resolveTelegramReasoningLevel({
		cfg,
		sessionKey: ctxPayload.SessionKey,
		agentId: route.agentId,
		loadFreshSessionStore
	});
	const forceBlockStreamingForReasoning = resolvedReasoningLevel === "on";
	const streamReasoningDraft = resolvedReasoningLevel === "stream";
	const streamDeliveryEnabled = !isRoomEvent && streamMode !== "off";
	const rawReplyQuoteText = ctxPayload.ReplyToIsQuote && typeof ctxPayload.ReplyToQuoteText === "string" ? ctxPayload.ReplyToQuoteText : void 0;
	const replyQuoteText = ctxPayload.ReplyToIsQuote ? rawReplyQuoteText?.trim() ? rawReplyQuoteText : ctxPayload.ReplyToBody?.trim() || void 0 : void 0;
	const replyQuoteMessageId = replyQuoteText && !ctxPayload.ReplyToIsExternal ? resolveTelegramReplyId(ctxPayload.ReplyToId) : void 0;
	const replyQuoteByMessageId = {};
	if (replyToMode !== "off") {
		if (replyQuoteText && replyQuoteMessageId != null) addTelegramNativeQuoteCandidate(replyQuoteByMessageId, replyQuoteMessageId, {
			text: replyQuoteText,
			...typeof ctxPayload.ReplyToQuotePosition === "number" ? { position: ctxPayload.ReplyToQuotePosition } : {},
			...Array.isArray(ctxPayload.ReplyToQuoteEntities) ? { entities: ctxPayload.ReplyToQuoteEntities } : {}
		});
		addTelegramNativeQuoteCandidate(replyQuoteByMessageId, ctxPayload.MessageSid ?? msg.message_id, buildTelegramNativeQuoteCandidate(getTelegramTextParts(msg)));
		if (!ctxPayload.ReplyToIsExternal && typeof ctxPayload.ReplyToQuoteSourceText === "string") addTelegramNativeQuoteCandidate(replyQuoteByMessageId, ctxPayload.ReplyToId, buildTelegramNativeQuoteCandidate({
			text: ctxPayload.ReplyToQuoteSourceText,
			entities: Array.isArray(ctxPayload.ReplyToQuoteSourceEntities) ? ctxPayload.ReplyToQuoteSourceEntities : void 0
		}));
	}
	const canStreamAnswerDraft = streamDeliveryEnabled && !(replyToMode !== "off" && replyQuoteText != null) && !accountBlockStreamingEnabled && !forceBlockStreamingForReasoning;
	const streamReasoningInProgressDraft = streamReasoningDraft && streamMode === "progress" && canStreamAnswerDraft;
	const canStreamReasoningDraft = !isRoomEvent && streamReasoningDraft && !streamReasoningInProgressDraft;
	const draftReplyToMessageId = replyToMode !== "off" && typeof msg.message_id === "number" ? replyQuoteMessageId ?? msg.message_id : void 0;
	const draftMinInitialChars = streamMode === "progress" ? 0 : DRAFT_MIN_INITIAL_CHARS;
	const progressSeed = `${route.accountId}:${chatId}:${threadSpec.id ?? ""}`;
	const mediaLocalRoots = getAgentScopedMediaLocalRoots(cfg, route.agentId);
	const createDraftLane = (laneName, enabled) => {
		return {
			stream: enabled ? (telegramDeps.createTelegramDraftStream ?? createTelegramDraftStream)({
				api: bot.api,
				chatId,
				maxChars: draftMaxChars,
				thread: threadSpec,
				replyToMessageId: draftReplyToMessageId,
				minInitialChars: draftMinInitialChars,
				renderText: renderStreamText,
				onSupersededPreview: (superseded) => {
					if (superseded.retain) {
						lanes[laneName].activeChunkIndex += 1;
						return;
					}
					bot.api.deleteMessage(chatId, superseded.messageId).catch((err) => {
						logVerbose(`telegram: superseded ${laneName} stream cleanup failed (${superseded.messageId}): ${String(err)}`);
					});
				},
				log: logVerbose,
				warn: logVerbose
			}) : void 0,
			lastPartialText: "",
			hasStreamedMessage: false,
			finalized: false,
			activeChunkIndex: 0
		};
	};
	const lanes = {
		answer: createDraftLane("answer", canStreamAnswerDraft),
		reasoning: createDraftLane("reasoning", canStreamReasoningDraft)
	};
	const answerLane = lanes.answer;
	const reasoningLane = lanes.reasoning;
	const nativeToolProgressDraft = Boolean(answerLane.stream) && resolveChannelStreamingPreviewToolProgress(telegramCfg) && !isRoomEvent && !isGroup && threadSpec.scope === "dm" && canUseNativeToolProgressDraftForChat({
		telegramCfg,
		chatId
	}) ? (telegramDeps.createNativeTelegramToolProgressDraft ?? createNativeTelegramToolProgressDraft)({
		api: bot.api,
		chatId,
		thread: threadSpec,
		log: logVerbose
	}) : void 0;
	let lastAnswerPartialText = "";
	let activeAnswerDraftIsToolProgressOnly = false;
	let activeAnswerBlockAssistantMessageIndex;
	let lastAnswerBlockPayload;
	let lastAnswerBlockText;
	let lastAnswerBlockButtons;
	let materializeAnswerLaneBeforeRotation;
	const queuedAnswerBlockRotations = [];
	let queuedAnswerBlockAssistantMessageIndex;
	let pendingAnswerBlockAssistantMessageIndex;
	let rotateAnswerLaneWhenQueuedBlocksSettle = false;
	function resetAnswerToolProgressDraft() {
		activeAnswerDraftIsToolProgressOnly = false;
	}
	async function prepareAnswerLaneForToolProgress() {
		if (answerLane.finalized) {
			answerLane.stream?.forceNewMessage();
			resetDraftLaneState(answerLane);
		}
		if (activeAnswerDraftIsToolProgressOnly) return;
		if (answerLane.hasStreamedMessage) await rotateAnswerLaneForNewMessage();
		activeAnswerDraftIsToolProgressOnly = true;
	}
	const progressDraft = createChannelProgressDraftCompositor({
		entry: telegramCfg,
		mode: streamMode,
		active: Boolean(answerLane.stream),
		seed: progressSeed,
		formatLine: formatTelegramProgressLine,
		update: async (streamText, options) => {
			await prepareAnswerLaneForToolProgress();
			answerLane.lastPartialText = streamText;
			answerLane.hasStreamedMessage = true;
			answerLane.finalized = false;
			answerLane.stream?.update(streamText);
			if (options?.flush) await answerLane.stream?.flush();
		},
		tryNativeUpdate: nativeToolProgressDraft ? async (streamText) => await nativeToolProgressDraft.update(streamText) : void 0
	});
	let finalAnswerDeliveryStarted = false;
	let finalAnswerDelivered = false;
	let verboseProgressActive = () => false;
	const pushStreamToolProgress = async (line, options) => {
		if (!answerLane.stream || answerLane.finalized || finalAnswerDeliveryStarted || finalAnswerDelivered) return false;
		return await progressDraft.pushToolProgress(line, options);
	};
	const pushStreamReasoningProgress = async (payload) => {
		return await progressDraft.pushReasoningProgress(payload.text, { snapshot: payload.isReasoningSnapshot === true });
	};
	const markProgressFinalStarted = () => {
		finalAnswerDeliveryStarted = true;
		progressDraft.markFinalReplyStarted();
	};
	const markProgressFinalDelivered = () => {
		finalAnswerDelivered = true;
		progressDraft.markFinalReplyDelivered();
	};
	const resetProgressDraftState = () => {
		progressDraft.reset();
	};
	const suppressProgressDraftState = () => {
		progressDraft.suppress();
	};
	let splitReasoningOnNextStream = false;
	let draftLaneEventQueue = Promise.resolve();
	const reasoningStepState = createTelegramReasoningStepState();
	const enqueueDraftLaneEvent = (task) => {
		draftLaneEventQueue = draftLaneEventQueue.then(async () => {
			if (isDispatchSuperseded()) return;
			await task();
		}).catch((err) => {
			logVerbose(`telegram: draft lane callback failed: ${String(err)}`);
		});
		return draftLaneEventQueue;
	};
	const splitTextIntoLaneSegments = (update, isReasoning) => {
		const split = splitTelegramReasoningText(update.text, isReasoning);
		const splitSegments = [];
		const useDelta = !update.replace && update.isReasoningSnapshot !== true && update.delta !== void 0;
		const segments = [];
		const suppressReasoning = resolvedReasoningLevel === "off";
		if (split.reasoningText && !suppressReasoning) splitSegments.push({
			lane: "reasoning",
			text: split.reasoningText
		});
		if (split.answerText) splitSegments.push({
			lane: "answer",
			text: split.answerText
		});
		for (const segment of splitSegments) {
			const canApplyDelta = useDelta && splitSegments.length === 1;
			segments.push({
				lane: segment.lane,
				update: {
					text: segment.text,
					...canApplyDelta ? { delta: update.delta } : {},
					...update.replace ? { replace: true } : {},
					...update.isReasoningSnapshot ? { isReasoningSnapshot: true } : {}
				}
			});
		}
		return {
			segments,
			suppressedReasoningOnly: Boolean(split.reasoningText) && suppressReasoning && !split.answerText
		};
	};
	const resetDraftLaneState = (lane) => {
		lane.lastPartialText = "";
		if (lane === answerLane) lastAnswerPartialText = "";
		lane.hasStreamedMessage = false;
		lane.finalized = false;
		lane.activeChunkIndex = 0;
		if (lane === answerLane) {
			resetAnswerToolProgressDraft();
			pendingAnswerBlockAssistantMessageIndex = void 0;
			lastAnswerBlockPayload = void 0;
			lastAnswerBlockText = void 0;
			lastAnswerBlockButtons = void 0;
		}
	};
	const rotateLaneForNewMessage = async (lane) => {
		if (!lane.hasStreamedMessage && typeof lane.stream?.messageId() !== "number") {
			resetDraftLaneState(lane);
			return;
		}
		await lane.stream?.stop();
		lane.stream?.forceNewMessage();
		resetDraftLaneState(lane);
	};
	const rotateAnswerLaneForNewMessage = async () => {
		if (materializeAnswerLaneBeforeRotation) await materializeAnswerLaneBeforeRotation();
		await rotateLaneForNewMessage(answerLane);
	};
	const rotateAnswerLaneAfterToolProgress = async () => {
		nativeToolProgressDraft?.stop();
		if (!activeAnswerDraftIsToolProgressOnly) return false;
		await answerLane.stream?.clear();
		answerLane.stream?.forceNewMessage();
		resetDraftLaneState(answerLane);
		suppressProgressDraftState();
		rotateAnswerLaneWhenQueuedBlocksSettle = false;
		return true;
	};
	const rotateAnswerLaneAfterQueuedBlocksSettle = async () => {
		if (!rotateAnswerLaneWhenQueuedBlocksSettle || queuedAnswerBlockRotations.length > 0) return false;
		rotateAnswerLaneWhenQueuedBlocksSettle = false;
		if (!answerLane.hasStreamedMessage || activeAnswerDraftIsToolProgressOnly) return false;
		await rotateAnswerLaneForNewMessage();
		return true;
	};
	const prepareAnswerLaneForText = async () => {
		nativeToolProgressDraft?.stop();
		if (await rotateAnswerLaneAfterToolProgress()) return true;
		if (await rotateAnswerLaneAfterQueuedBlocksSettle()) return true;
		if (!answerLane.finalized) return false;
		answerLane.stream?.forceNewMessage();
		resetDraftLaneState(answerLane);
		rotateAnswerLaneWhenQueuedBlocksSettle = false;
		return true;
	};
	const prepareQueuedAnswerBlock = async (payload, blockContext) => {
		if (!splitTextIntoLaneSegments({ text: payload.text }, payload.isReasoning).segments.some((segment) => segment.lane === "answer")) return;
		resetProgressDraftState();
		const assistantMessageIndex = blockContext?.assistantMessageIndex;
		if (assistantMessageIndex === void 0) {
			queuedAnswerBlockRotations.push({
				text: payload.text,
				shouldRotateBeforeDelivery: false
			});
			return;
		}
		const previousAssistantMessageIndex = queuedAnswerBlockAssistantMessageIndex ?? activeAnswerBlockAssistantMessageIndex ?? pendingAnswerBlockAssistantMessageIndex;
		const shouldRotateBeforeDelivery = previousAssistantMessageIndex !== void 0 && assistantMessageIndex !== previousAssistantMessageIndex;
		queuedAnswerBlockRotations.push({
			assistantMessageIndex,
			text: payload.text,
			shouldRotateBeforeDelivery
		});
		queuedAnswerBlockAssistantMessageIndex = assistantMessageIndex;
	};
	const recomputeQueuedAnswerBlockRotations = () => {
		let previousAssistantMessageIndex = activeAnswerBlockAssistantMessageIndex ?? pendingAnswerBlockAssistantMessageIndex;
		queuedAnswerBlockAssistantMessageIndex = void 0;
		for (const entry of queuedAnswerBlockRotations) {
			if (entry.assistantMessageIndex === void 0) continue;
			entry.shouldRotateBeforeDelivery = previousAssistantMessageIndex !== void 0 && entry.assistantMessageIndex !== previousAssistantMessageIndex;
			previousAssistantMessageIndex = entry.assistantMessageIndex;
			queuedAnswerBlockAssistantMessageIndex = entry.assistantMessageIndex;
		}
	};
	const queuedAnswerBlockRotationTextMatchesPayload = (entry, payload) => {
		return entry.text !== void 0 && payload.text !== void 0 && entry.text === payload.text;
	};
	const queuedAnswerBlockRotationMatchesDelivery = (entry, payload, assistantMessageIndex) => {
		if (assistantMessageIndex !== void 0 && entry.assistantMessageIndex !== void 0) return assistantMessageIndex === entry.assistantMessageIndex;
		return queuedAnswerBlockRotationTextMatchesPayload(entry, payload);
	};
	const takeQueuedAnswerBlockRotation = (payload, assistantMessageIndex) => {
		if (queuedAnswerBlockRotations.length === 0) return false;
		const matchIndex = queuedAnswerBlockRotations.findIndex((entry) => queuedAnswerBlockRotationMatchesDelivery(entry, payload, assistantMessageIndex));
		const consumeIndex = Math.max(matchIndex, 0);
		const matchedEntry = queuedAnswerBlockRotations.splice(0, consumeIndex + 1).at(-1);
		const shouldRotateBeforeDelivery = matchedEntry?.shouldRotateBeforeDelivery ?? false;
		if (matchedEntry?.assistantMessageIndex !== void 0) {
			activeAnswerBlockAssistantMessageIndex = matchedEntry.assistantMessageIndex;
			pendingAnswerBlockAssistantMessageIndex = void 0;
		}
		recomputeQueuedAnswerBlockRotations();
		return shouldRotateBeforeDelivery;
	};
	const dropQueuedAnswerBlockRotation = (payload, assistantMessageIndex) => {
		let matchIndex = queuedAnswerBlockRotations.findIndex((entry) => queuedAnswerBlockRotationMatchesDelivery(entry, payload, assistantMessageIndex));
		if (matchIndex < 0 && assistantMessageIndex === void 0) matchIndex = queuedAnswerBlockRotations.findIndex((entry) => entry.assistantMessageIndex === void 0);
		if (matchIndex >= 0) {
			const matchedEntry = queuedAnswerBlockRotations[matchIndex];
			queuedAnswerBlockRotations.splice(matchIndex, 1);
			if (matchIndex === 0 && matchedEntry?.assistantMessageIndex !== void 0 && rotateAnswerLaneWhenQueuedBlocksSettle && activeAnswerBlockAssistantMessageIndex === void 0 && answerLane.hasStreamedMessage) pendingAnswerBlockAssistantMessageIndex = matchedEntry.assistantMessageIndex;
			recomputeQueuedAnswerBlockRotations();
		}
	};
	const updateDraftFromPartial = (lane, update) => {
		const laneStream = lane.stream;
		if (!laneStream || !update.text) return;
		const nextText = resolveDraftPartialText(lane === answerLane ? lastAnswerPartialText : lane.lastPartialText, update);
		if (!nextText) return;
		if (lane === answerLane) {
			if (streamMode === "progress") return;
			resetAnswerToolProgressDraft();
			suppressProgressDraftState();
		}
		lane.hasStreamedMessage = true;
		lane.finalized = false;
		if (lane === answerLane) lastAnswerPartialText = nextText;
		lane.lastPartialText = nextText;
		laneStream.update(nextText);
	};
	const ingestDraftLaneSegments = async (update, isReasoning) => {
		const split = splitTextIntoLaneSegments(update, isReasoning);
		for (const segment of split.segments) {
			if (segment.lane === "answer") await prepareAnswerLaneForText();
			if (segment.lane === "reasoning") {
				reasoningStepState.noteReasoningHint();
				reasoningStepState.noteReasoningDelivered();
			}
			updateDraftFromPartial(lanes[segment.lane], segment.update);
		}
	};
	const flushDraftLane = async (lane) => {
		if (!lane.stream) return;
		await lane.stream.flush();
	};
	const resolvedBlockStreamingEnabled = resolveChannelStreamingBlockEnabled(telegramCfg);
	const disableBlockStreaming = !streamDeliveryEnabled ? true : forceBlockStreamingForReasoning ? false : typeof resolvedBlockStreamingEnabled === "boolean" ? !resolvedBlockStreamingEnabled : canStreamAnswerDraft ? true : void 0;
	const chunkMode = resolveChunkMode(cfg, "telegram", route.accountId);
	const supersedeReplyFence = shouldSupersedeTelegramReplyFence(ctxPayload);
	activeReplyFenceKey = supersedeReplyFence ? replyFenceKey.activeKey : buildTelegramNonInterruptingReplyFenceKey({
		activeKey: replyFenceKey.activeKey,
		laneKey: scopedReplyFenceLaneKey
	});
	if (!isRoomEvent && supersedeReplyFence) supersedeTelegramReplyFence(replyFenceKey.roomEventKey);
	replyFenceGeneration = beginTelegramReplyFence({
		key: activeReplyFenceKey,
		supersede: supersedeReplyFence,
		abortController: replyAbortController,
		laneKey: scopedReplyFenceLaneKey
	});
	const implicitQuoteReplyTargetId = replyQuoteMessageId != null ? String(replyQuoteMessageId) : void 0;
	const currentMessageIdForQuoteReply = implicitQuoteReplyTargetId && ctxPayload.MessageSid ? ctxPayload.MessageSid : void 0;
	const replyQuotePosition = typeof ctxPayload.ReplyToQuotePosition === "number" ? ctxPayload.ReplyToQuotePosition : void 0;
	const replyQuoteEntities = Array.isArray(ctxPayload.ReplyToQuoteEntities) ? ctxPayload.ReplyToQuoteEntities : void 0;
	const deliveryState = createLaneDeliveryStateTracker();
	const clearGroupHistory = () => {
		if (isGroup && historyKey) createChannelHistoryWindow({ historyMap: groupHistories }).clear({
			historyKey,
			limit: historyLimit
		});
	};
	const beginDeliveryCorrelation = () => beginTelegramInboundEventDeliveryCorrelation(ctxPayload.SessionKey, {
		outboundTo: historyKey || String(chatId),
		outboundAccountId: route.accountId,
		markInboundEventDelivered: () => {
			deliveryState.markDelivered();
			if (isRoomEvent) clearGroupHistory();
		}
	}, { inboundEventKind: ctxPayload.InboundEventKind });
	const endTelegramInboundEventDeliveryCorrelation = beginDeliveryCorrelation();
	const sessionKey = ctxPayload.SessionKey;
	const resolveCurrentTurnTranscriptFinalText = async () => {
		if (!sessionKey) return;
		try {
			const { storePath, store } = loadFreshSessionStore(route.agentId);
			const sessionEntry = resolveSessionStoreEntry({
				store,
				sessionKey
			}).existing;
			if (!sessionEntry?.sessionId) return;
			const { sessionFile } = await resolveAndPersistSessionFile({
				sessionId: sessionEntry.sessionId,
				sessionKey,
				sessionStore: store,
				storePath,
				sessionEntry,
				agentId: route.agentId,
				sessionsDir: path.dirname(storePath)
			});
			const latest = await readLatestAssistantTextFromSessionTranscript(sessionFile);
			if (!latest?.timestamp || latest.timestamp < dispatchStartedAt) return;
			return latest.text;
		} catch (err) {
			logVerbose(`telegram transcript final candidate lookup failed: ${formatErrorMessage(err)}`);
			return;
		}
	};
	const deliveryBaseOptions = {
		chatId: String(chatId),
		accountId: route.accountId,
		sessionKeyForInternalHooks: ctxPayload.SessionKey,
		mirrorIsGroup: isGroup,
		mirrorGroupId: isGroup ? String(chatId) : void 0,
		token: opts.token,
		runtime,
		bot,
		mediaLocalRoots,
		mediaMaxBytes: (opts.mediaMaxMb ?? telegramCfg.mediaMaxMb ?? 100) * 1024 * 1024,
		replyToMode,
		textLimit,
		thread: threadSpec,
		tableMode,
		chunkMode,
		linkPreview: telegramCfg.linkPreview,
		replyQuoteMessageId,
		replyQuoteText,
		replyQuotePosition,
		replyQuoteEntities,
		replyQuoteByMessageId,
		transcriptMirror: sessionKey ? async (payload) => {
			await mirrorTelegramAssistantReplyToTranscript({
				cfg,
				route,
				sessionKey,
				loadFreshSessionStore,
				payload
			});
		} : void 0
	};
	const silentErrorReplies = telegramCfg.silentErrorReplies === true;
	const isDmTopic = !isGroup && threadSpec.scope === "dm" && threadSpec.id != null;
	let queuedFinal = false;
	let skippedDuplicateAnswerBlockDraftDelivery = false;
	let suppressSilentReplyFallback = false;
	let hadErrorReplyFailureOrSkip = false;
	let isFirstTurnInSession = false;
	let dispatchError;
	try {
		const sticker = ctxPayload.Sticker;
		if (sticker?.fileId && sticker.fileUniqueId && ctxPayload.MediaPath) {
			const agentDir = resolveAgentDir(cfg, route.agentId);
			const stickerSupportsVision = await resolveStickerVisionSupport(cfg, route.agentId);
			let description = sticker.cachedDescription ?? null;
			if (!description) description = await describeStickerImage({
				imagePath: ctxPayload.MediaPath,
				cfg,
				agentDir,
				agentId: route.agentId
			});
			if (description) {
				const stickerContext = [sticker.emoji, sticker.setName ? `from "${sticker.setName}"` : null].filter(Boolean).join(" ");
				const formattedDesc = `[Sticker${stickerContext ? ` ${stickerContext}` : ""}] ${description}`;
				sticker.cachedDescription = description;
				if (!stickerSupportsVision) {
					ctxPayload.Body = formattedDesc;
					ctxPayload.BodyForAgent = formattedDesc;
					pruneStickerMediaFromContext(ctxPayload, { stickerMediaIncluded: ctxPayload.StickerMediaIncluded });
				}
				cacheSticker({
					fileId: sticker.fileId,
					fileUniqueId: sticker.fileUniqueId,
					emoji: sticker.emoji,
					setName: sticker.setName,
					description,
					cachedAt: (/* @__PURE__ */ new Date()).toISOString(),
					receivedFrom: ctxPayload.From
				});
				logVerbose(`telegram: cached sticker description for ${sticker.fileUniqueId}`);
			}
		}
		const applyTextToPayload = (payload, text) => {
			if (payload.text === text) return payload;
			return {
				...payload,
				text
			};
		};
		const applyTextToFollowUpPayload = (payload, text) => {
			const { replyToId: _replyToId, replyToCurrent: _replyToCurrent, replyToTag: _replyToTag, ...followUp } = applyTextToPayload(payload, text);
			return followUp;
		};
		const splitFinalTextForStream = (text) => {
			return (chunkMode === "newline" ? chunkMarkdownTextWithMode(text, draftMaxChars, chunkMode) : [text]).flatMap((chunk) => markdownToTelegramChunks(chunk, draftMaxChars, { tableMode }).map((telegramChunk) => telegramChunk.text));
		};
		const applyQuoteReplyTarget = (payload) => {
			if (!implicitQuoteReplyTargetId || !currentMessageIdForQuoteReply || payload.replyToId !== currentMessageIdForQuoteReply || payload.replyToTag || payload.replyToCurrent) return payload;
			return {
				...payload,
				replyToId: implicitQuoteReplyTargetId
			};
		};
		const usesNativeTelegramQuote = (payload) => {
			if (replyQuoteText != null) return true;
			return payload.replyToId != null && replyQuoteByMessageId[payload.replyToId] != null;
		};
		const sendPayload = async (payload, options) => {
			if (isDispatchSuperseded()) return false;
			const deliverablePayload = applyQuoteReplyTarget(payload);
			const silent = options?.silent ?? (silentErrorReplies && payload.isError === true);
			const durableDelivery = telegramDeps.deliverInboundReplyWithMessageSendContext;
			if (options?.durable && durableDelivery) {
				const durable = await durableDelivery({
					cfg,
					channel: "telegram",
					to: String(chatId),
					accountId: route.accountId,
					agentId: route.agentId,
					ctxPayload,
					payload: deliverablePayload,
					info: { kind: "final" },
					replyToMode,
					threadId: threadSpec.id,
					formatting: {
						textLimit,
						tableMode,
						chunkMode
					},
					silent,
					requiredCapabilities: deriveDurableFinalDeliveryRequirements({
						payload: deliverablePayload,
						replyToId: deliverablePayload.replyToId,
						threadId: threadSpec.id,
						silent,
						payloadTransport: true,
						extraCapabilities: { nativeQuote: usesNativeTelegramQuote(deliverablePayload) }
					})
				});
				if (durable.status === "failed") throw durable.error;
				if (durable.status === "handled_visible") {
					deliveryState.markDelivered();
					return true;
				}
				if (durable.status === "handled_no_send") return false;
			}
			const result = await (telegramDeps.deliverReplies ?? deliverReplies)({
				...deliveryBaseOptions,
				transcriptMirror: options?.durable ? deliveryBaseOptions.transcriptMirror : void 0,
				replies: [deliverablePayload],
				onVoiceRecording: sendRecordVoice,
				silent,
				mediaLoader: telegramDeps.loadWebMedia
			});
			if (result.delivered) deliveryState.markDelivered();
			return result.delivered;
		};
		const emitPreviewFinalizedHook = async (result) => {
			if (isDispatchSuperseded() || result.kind !== "preview-finalized") return;
			(telegramDeps.emitInternalMessageSentHook ?? emitInternalMessageSentHook)({
				sessionKeyForInternalHooks: deliveryBaseOptions.sessionKeyForInternalHooks,
				chatId: deliveryBaseOptions.chatId,
				accountId: deliveryBaseOptions.accountId,
				content: result.delivery.content,
				success: true,
				messageId: result.delivery.messageId,
				isGroup: deliveryBaseOptions.mirrorIsGroup,
				groupId: deliveryBaseOptions.mirrorGroupId
			});
			try {
				await (telegramDeps.recordOutboundMessageForPromptContext ?? recordOutboundMessageForPromptContext)({
					cfg,
					account: { accountId: route.accountId },
					chatId: deliveryBaseOptions.chatId,
					message: { message_id: result.delivery.messageId },
					messageId: result.delivery.messageId,
					text: result.delivery.promptContextContent ?? result.delivery.content,
					...threadSpec.id !== void 0 ? { messageThreadId: threadSpec.id } : {}
				});
			} catch (error) {
				logVerbose(`telegram: failed to record streamed reply for prompt context: ${formatErrorMessage(error)}`);
			}
			if (deliveryBaseOptions.transcriptMirror && result.delivery.content) deliveryBaseOptions.transcriptMirror({ text: result.delivery.content }).catch((err) => {
				logVerbose(`telegram preview-finalized transcriptMirror failed: ${formatErrorMessage(err)}`);
			});
		};
		const finalizeSkippedDuplicateAnswerBlockDraft = async () => {
			if (!skippedDuplicateAnswerBlockDraftDelivery || queuedFinal || dispatchError || isDispatchSuperseded() || answerLane.finalized) return;
			const stream = answerLane.stream;
			const content = answerLane.lastPartialText;
			if (!stream || !content) return;
			await stream.stop();
			const messageId = stream.messageId();
			if (typeof messageId !== "number") {
				if (stream.sendMayHaveLanded?.()) {
					answerLane.finalized = true;
					deliveryState.markDelivered();
				}
				return;
			}
			answerLane.finalized = true;
			deliveryState.markDelivered();
			await emitPreviewFinalizedHook({
				kind: "preview-finalized",
				delivery: {
					content,
					promptContextContent: content,
					messageId,
					buttonsAttached: false,
					receipt: createPreviewMessageReceipt({ id: messageId })
				}
			});
		};
		const deliverLaneText = createLaneTextDeliverer({
			lanes,
			draftMaxChars,
			applyTextToPayload,
			applyTextToFollowUpPayload,
			splitFinalTextForStream,
			sendPayload,
			flushDraftLane,
			stopDraftLane: async (lane) => {
				await lane.stream?.stop();
			},
			clearDraftLane: async (lane) => {
				await lane.stream?.clear();
			},
			editStreamMessage: async ({ messageId, text, buttons }) => {
				if (isDispatchSuperseded()) return;
				await (telegramDeps.editMessageTelegram ?? editMessageTelegram)(chatId, messageId, text, {
					api: bot.api,
					cfg,
					accountId: route.accountId,
					linkPreview: telegramCfg.linkPreview,
					buttons
				});
			},
			resolveFinalTextCandidate: () => resolveCurrentTurnTranscriptFinalText(),
			log: logVerbose,
			markDelivered: () => {
				deliveryState.markDelivered();
			}
		});
		materializeAnswerLaneBeforeRotation = async () => {
			if (!lastAnswerBlockPayload || !answerLane.stream || !answerLane.hasStreamedMessage || answerLane.finalized || activeAnswerDraftIsToolProgressOnly) return false;
			const text = answerLane.lastPartialText || lastAnswerPartialText || lastAnswerBlockText;
			if (!text?.trim()) return false;
			const wasSkippedDuplicate = skippedDuplicateAnswerBlockDraftDelivery;
			skippedDuplicateAnswerBlockDraftDelivery = false;
			const deliveredText = answerLane.stream.lastDeliveredText?.();
			const messageId = answerLane.stream.messageId();
			if (!lastAnswerBlockButtons && !wasSkippedDuplicate && deliveredText === text.trimEnd() && typeof messageId === "number") {
				await answerLane.stream.stop();
				answerLane.finalized = true;
				deliveryState.markDelivered();
				await emitPreviewFinalizedHook({
					kind: "preview-finalized",
					delivery: {
						content: text,
						promptContextContent: deliveredText,
						messageId,
						receipt: createPreviewMessageReceipt({ id: messageId })
					}
				});
				return true;
			}
			const result = await deliverLaneText({
				laneName: "answer",
				text,
				payload: lastAnswerBlockPayload,
				infoKind: "block",
				buttons: lastAnswerBlockButtons,
				finalizePreview: true,
				durable: false
			});
			await emitPreviewFinalizedHook(result);
			return result.kind !== "skipped";
		};
		const deliverProgressModeFinalAnswer = async (payload, text) => {
			if (activeAnswerDraftIsToolProgressOnly) await rotateAnswerLaneAfterToolProgress();
			else {
				await answerLane.stream?.clear();
				resetDraftLaneState(answerLane);
			}
			if (!await sendPayload(applyTextToPayload(payload, text), { durable: true })) return { kind: "skipped" };
			answerLane.finalized = true;
			markProgressFinalDelivered();
			return { kind: "sent" };
		};
		const resolveTranscriptBackedFinalText = async (text) => await resolveTranscriptBackedChannelFinalText({
			finalText: text,
			resolveCandidateText: resolveCurrentTurnTranscriptFinalText
		});
		if (isDmTopic) try {
			const { store } = loadFreshSessionStore(route.agentId);
			const sessionKeyLocal = ctxPayload.SessionKey;
			if (sessionKeyLocal) isFirstTurnInSession = !resolveSessionStoreEntry({
				store,
				sessionKey: sessionKeyLocal
			}).existing?.systemSent;
			else logVerbose("auto-topic-label: SessionKey is absent, skipping first-turn detection");
		} catch (err) {
			logVerbose(`auto-topic-label: session store error: ${formatErrorMessage(err)}`);
		}
		loadFreshSessionStore.clear();
		if (statusReactionController && !isRoomEvent) statusReactionController.setThinking();
		const { onModelSelected, ...replyPipeline } = (telegramDeps.createChannelMessageReplyPipeline ?? createChannelReplyPipeline)({
			cfg,
			agentId: route.agentId,
			channel: "telegram",
			accountId: route.accountId,
			typing: {
				start: sendTyping,
				onStartError: (err) => {
					logTypingFailure({
						log: logVerbose,
						channel: "telegram",
						target: String(chatId),
						error: err
					});
				}
			}
		});
		try {
			const turnResult = await runChannelInboundEvent({
				channel: "telegram",
				accountId: route.accountId,
				raw: dispatchContext,
				adapter: {
					ingest: () => ({
						id: ctxPayload.MessageSid ?? `${chatId}:${Date.now()}`,
						timestamp: typeof ctxPayload.Timestamp === "number" ? ctxPayload.Timestamp : void 0,
						rawText: ctxPayload.RawBody ?? "",
						textForAgent: ctxPayload.BodyForAgent,
						textForCommands: ctxPayload.CommandBody,
						raw: dispatchContext
					}),
					resolveTurn: () => ({
						channel: "telegram",
						accountId: route.accountId,
						routeSessionKey: route.sessionKey,
						storePath: dispatchContext.turn.storePath,
						ctxPayload,
						recordInboundSession: dispatchContext.turn.recordInboundSession,
						record: dispatchContext.turn.record,
						runDispatch: () => {
							const sentBlockMediaUrls = /* @__PURE__ */ new Set();
							return telegramDeps.dispatchReplyWithBufferedBlockDispatcher({
								ctx: ctxPayload,
								cfg,
								dispatcherOptions: {
									...replyPipeline,
									beforeDeliver: async (payload) => payload,
									onBeforeDeliverCancelled: (payload, info) => {
										if (info.kind === "block") return enqueueDraftLaneEvent(async () => {
											dropQueuedAnswerBlockRotation(payload, info.assistantMessageIndex);
										});
									},
									deliver: async (payload, info) => {
										if (isDispatchSuperseded()) return;
										const deduped = info.kind === "final" ? deduplicateBlockSentMedia(payload, sentBlockMediaUrls) : payload;
										if (deduped === void 0) return;
										const effectivePayload = deduped;
										if (shouldSuppressLocalTelegramExecApprovalPrompt({
											cfg,
											accountId: route.accountId,
											payload
										})) {
											queuedFinal = true;
											return;
										}
										const telegramButtons = resolvePayloadTelegramInlineButtons(effectivePayload);
										const split = splitTextIntoLaneSegments({ text: effectivePayload.text }, payload.isReasoning);
										const segments = split.segments;
										const reply = resolveSendableOutboundReplyParts(effectivePayload);
										if (info.kind === "final" && (reply.text.length > 0 || reply.hasMedia)) markProgressFinalStarted();
										if (info.kind === "final") await enqueueDraftLaneEvent(async () => {});
										const isToolPayloadAfterFinal = info.kind === "tool" && (finalAnswerDeliveryStarted || finalAnswerDelivered);
										const isNonTerminalWarningAfterDeliveredFinal = isReplyPayloadNonTerminalToolErrorWarning(effectivePayload) && finalAnswerDelivered;
										if ((isToolPayloadAfterFinal || isNonTerminalWarningAfterDeliveredFinal) && !reply.hasMedia && !hasExecApprovalPayload(effectivePayload)) return;
										if (payload.isError === true) hadErrorReplyFailureOrSkip = true;
										const deliverFinalAnswerText = async (answerPayload, text, buttons) => {
											const finalText = await resolveTranscriptBackedFinalText(text);
											const deliverPostFinalFollowUpText = async () => {
												await prepareAnswerLaneForText();
												return deliverLaneText({
													laneName: "answer",
													text: finalText,
													payload: answerPayload,
													infoKind: "final",
													buttons
												});
											};
											if (finalAnswerDelivered) return deliverPostFinalFollowUpText();
											if (streamMode === "progress") return deliverProgressModeFinalAnswer(answerPayload, finalText);
											if (!await rotateAnswerLaneAfterToolProgress()) await rotateAnswerLaneAfterQueuedBlocksSettle();
											const result = await deliverLaneText({
												laneName: "answer",
												text: finalText,
												payload: answerPayload,
												infoKind: "final",
												buttons
											});
											if (result.kind !== "skipped") markProgressFinalDelivered();
											return result;
										};
										const flushBufferedFinalAnswer = async () => {
											const buffered = reasoningStepState.takeBufferedFinalAnswer(replyFenceGeneration);
											if (!buffered) return;
											const bufferedButtons = resolvePayloadTelegramInlineButtons(buffered.payload);
											await deliverFinalAnswerText(buffered.payload, buffered.text, bufferedButtons);
											reasoningStepState.resetForNextStep();
										};
										let blockDelivered = false;
										const hasAnswerSegment = segments.some((segment) => segment.lane === "answer");
										if (info.kind === "block" && !hasAnswerSegment) dropQueuedAnswerBlockRotation(effectivePayload, info.assistantMessageIndex);
										for (const segment of segments) {
											if (segment.lane === "answer" && info.kind === "final" && reasoningStepState.shouldBufferFinalAnswer()) {
												reasoningStepState.bufferFinalAnswer({
													payload: effectivePayload,
													text: segment.update.text,
													bufferedGeneration: replyFenceGeneration
												});
												continue;
											}
											if (segment.lane === "reasoning") reasoningStepState.noteReasoningHint();
											if (segment.lane === "answer" && info.kind === "tool") {
												if (verboseProgressActive()) {
													if (await sendPayload(applyTextToPayload(effectivePayload, segment.update.text))) blockDelivered = true;
													continue;
												}
												const canRepresentAsTransientProgress = canUseNativeToolProgressDraft({
													payload: effectivePayload,
													reply,
													buttons: telegramButtons
												});
												if (nativeToolProgressDraft && canRepresentAsTransientProgress) {
													if (await pushStreamToolProgress(segment.update.text)) {
														blockDelivered = true;
														continue;
													}
												}
												if (canRepresentAsTransientProgress && streamMode === "progress" && answerLane.stream) continue;
												await prepareAnswerLaneForToolProgress();
											}
											const ownedByQueuedAnswerBlockRotation = queuedAnswerBlockRotations.some((entry) => queuedAnswerBlockRotationMatchesDelivery(entry, effectivePayload, info.assistantMessageIndex));
											if (streamMode === "partial" && info.kind === "block" && segment.lane === "answer" && !reply.hasMedia && !hasExecApprovalPayload(effectivePayload) && telegramButtons === void 0 && answerLane.hasStreamedMessage && !activeAnswerDraftIsToolProgressOnly && !ownedByQueuedAnswerBlockRotation && segment.update.text.trimEnd() === answerLane.lastPartialText.trimEnd()) {
												skippedDuplicateAnswerBlockDraftDelivery = true;
												lastAnswerBlockPayload = effectivePayload;
												lastAnswerBlockText = segment.update.text;
												lastAnswerBlockButtons = telegramButtons;
												resetAnswerToolProgressDraft();
												resetProgressDraftState();
												blockDelivered = true;
												continue;
											}
											if (segment.lane === "answer" && info.kind === "block") {
												const preparedAnswerLane = await prepareAnswerLaneForText();
												if (takeQueuedAnswerBlockRotation(effectivePayload, info.assistantMessageIndex) && !preparedAnswerLane) {
													await rotateAnswerLaneForNewMessage();
													rotateAnswerLaneWhenQueuedBlocksSettle = false;
												}
												resetAnswerToolProgressDraft();
												resetProgressDraftState();
											}
											const result = segment.lane === "answer" && info.kind === "final" ? await deliverFinalAnswerText(effectivePayload, segment.update.text, telegramButtons) : await deliverLaneText({
												laneName: segment.lane,
												text: segment.update.text,
												payload: effectivePayload,
												infoKind: info.kind,
												buttons: telegramButtons
											});
											if (segment.lane === "answer" && result.kind === "preview-finalized") await emitPreviewFinalizedHook(result);
											if (segment.lane === "answer" && info.kind === "block" && (result.kind === "preview-updated" || result.kind === "preview-finalized" || result.kind === "preview-retained")) {
												lastAnswerBlockPayload = effectivePayload;
												lastAnswerBlockText = segment.update.text;
												lastAnswerBlockButtons = telegramButtons;
											}
											blockDelivered = blockDelivered || result.kind !== "skipped";
											if (segment.lane === "reasoning") {
												if (result.kind !== "skipped") {
													reasoningStepState.noteReasoningDelivered();
													await flushBufferedFinalAnswer();
												}
												continue;
											}
											if (info.kind === "final") reasoningStepState.resetForNextStep();
										}
										const trackBlockMedia = (delivered) => {
											if (delivered && info.kind === "block" && payload.mediaUrls?.length) for (const url of payload.mediaUrls) sentBlockMediaUrls.add(url);
										};
										if (segments.length > 0) {
											trackBlockMedia(blockDelivered);
											return;
										}
										if (split.suppressedReasoningOnly) {
											let delivered = false;
											if (reply.hasMedia) delivered = await sendPayload(typeof effectivePayload.text === "string" ? {
												...effectivePayload,
												text: ""
											} : effectivePayload, { durable: info.kind === "final" });
											if (info.kind === "final" && delivered) markProgressFinalDelivered();
											if (info.kind === "final") await flushBufferedFinalAnswer();
											trackBlockMedia(delivered);
											return;
										}
										if (info.kind === "final") {
											await rotateAnswerLaneAfterToolProgress();
											await answerLane.stream?.stop();
											await reasoningLane.stream?.stop();
											reasoningStepState.resetForNextStep();
										}
										if (!(reply.hasMedia || reply.text.length > 0)) {
											if (info.kind === "final") await flushBufferedFinalAnswer();
											return;
										}
										const delivered = await sendPayload(effectivePayload, { durable: info.kind === "final" });
										if (info.kind === "final" && delivered) markProgressFinalDelivered();
										if (info.kind === "final") await flushBufferedFinalAnswer();
										trackBlockMedia(delivered);
									},
									onSkip: (payload, info) => {
										if (info.kind === "block") enqueueDraftLaneEvent(async () => {
											dropQueuedAnswerBlockRotation(payload, info.assistantMessageIndex);
										});
										if (payload.isError === true) hadErrorReplyFailureOrSkip = true;
										if (info.reason !== "silent") deliveryState.markNonSilentSkip();
									},
									onError: (err, info) => {
										const errorPolicy = resolveTelegramErrorPolicy({
											accountConfig: telegramCfg,
											groupConfig,
											topicConfig
										});
										if (isSilentErrorPolicy(errorPolicy.policy)) return;
										if (errorPolicy.policy === "once" && shouldSuppressTelegramError({
											scopeKey: buildTelegramErrorScopeKey({
												accountId: route.accountId,
												chatId,
												threadId: threadSpec.id
											}),
											cooldownMs: errorPolicy.cooldownMs,
											errorMessage: String(err)
										})) return;
										deliveryState.markNonSilentFailure();
										runtime.error?.(danger(`telegram ${info.kind} reply failed: ${String(err)}`));
									}
								},
								replyOptions: {
									skillFilter,
									disableBlockStreaming,
									abortSignal: replyAbortController.signal,
									sourceReplyDeliveryMode: isRoomEvent ? "message_tool_only" : void 0,
									queuedDeliveryCorrelations: isRoomEvent ? [{ begin: beginDeliveryCorrelation }] : void 0,
									queuedFollowupLifecycle: isRoomEvent ? {
										onEnqueued: () => {
											replyAbortControllerQueued = true;
										},
										onComplete: () => {
											replyAbortControllerQueued = false;
											releaseTelegramReplyFenceAbortController(activeReplyFenceKey, replyAbortController);
										}
									} : void 0,
									suppressTyping: isRoomEvent,
									onPartialReply: answerLane.stream || reasoningLane.stream ? (payload) => enqueueDraftLaneEvent(async () => {
										await ingestDraftLaneSegments(payload);
									}) : void 0,
									onBlockReplyQueued: answerLane.stream ? (payload, blockContext) => enqueueDraftLaneEvent(async () => {
										await prepareQueuedAnswerBlock(payload, blockContext);
									}) : void 0,
									onReasoningStream: reasoningLane.stream ? (payload) => enqueueDraftLaneEvent(async () => {
										if (splitReasoningOnNextStream) {
											reasoningLane.stream?.forceNewMessage();
											resetDraftLaneState(reasoningLane);
											splitReasoningOnNextStream = false;
										}
										await ingestDraftLaneSegments(payload, true);
									}) : streamReasoningInProgressDraft ? (payload) => enqueueDraftLaneEvent(async () => {
										await pushStreamReasoningProgress(payload);
									}) : void 0,
									onAssistantMessageStart: answerLane.stream ? () => enqueueDraftLaneEvent(async () => {
										reasoningStepState.resetForNextStep();
										finalAnswerDelivered = false;
										if (streamMode !== "progress") resetProgressDraftState();
										if (answerLane.finalized) {
											await rotateLaneForNewMessage(answerLane);
											rotateAnswerLaneWhenQueuedBlocksSettle = false;
										} else if (answerLane.hasStreamedMessage && !activeAnswerDraftIsToolProgressOnly) rotateAnswerLaneWhenQueuedBlocksSettle = true;
									}) : void 0,
									onReasoningEnd: reasoningLane.stream ? () => enqueueDraftLaneEvent(async () => {
										splitReasoningOnNextStream = reasoningLane.hasStreamedMessage;
										resetProgressDraftState();
									}) : void 0,
									suppressDefaultToolProgressMessages: !streamDeliveryEnabled || Boolean(answerLane.stream),
									allowProgressCallbacksWhenSourceDeliverySuppressed: !isRoomEvent && Boolean(answerLane.stream),
									onVerboseProgressVisibility: (isActive) => {
										verboseProgressActive = isActive;
									},
									commentaryProgressEnabled: streamMode === "progress" ? progressDraft.commentaryProgressEnabled : void 0,
									onToolStart: async (payload) => {
										const toolName = payload.name?.trim();
										const progressPromise = pushStreamToolProgress(formatChannelProgressDraftLineForEntry(telegramCfg, {
											event: "tool",
											name: toolName,
											phase: payload.phase,
											args: payload.args
										}, payload.detailMode ? { detailMode: payload.detailMode } : void 0), {
											toolName,
											startImmediately: true
										});
										if (statusReactionController && toolName) await statusReactionController.setTool(toolName);
										await progressPromise;
									},
									onItemEvent: async (payload) => {
										if (payload.kind === "preamble") {
											if (verboseProgressActive()) return;
											await progressDraft.pushCommentaryProgress(payload.progressText, { itemId: payload.itemId });
											return;
										}
										await pushStreamToolProgress(buildChannelProgressDraftLineForEntry(telegramCfg, {
											event: "item",
											itemId: payload.itemId,
											itemKind: payload.kind,
											title: payload.title,
											name: payload.name,
											phase: payload.phase,
											status: payload.status,
											summary: payload.summary,
											progressText: payload.progressText,
											meta: payload.meta
										}));
									},
									onPlanUpdate: async (payload) => {
										if (payload.phase !== "update") return;
										await pushStreamToolProgress(formatChannelProgressDraftLine({
											event: "plan",
											phase: payload.phase,
											title: payload.title,
											explanation: payload.explanation,
											steps: payload.steps
										}));
									},
									onApprovalEvent: async (payload) => {
										if (payload.phase !== "requested") return;
										await pushStreamToolProgress(formatChannelProgressDraftLine({
											event: "approval",
											phase: payload.phase,
											title: payload.title,
											command: payload.command,
											reason: payload.reason,
											message: payload.message
										}));
									},
									onCommandOutput: async (payload) => {
										if (payload.phase !== "end") return;
										await pushStreamToolProgress(formatChannelProgressDraftLine({
											event: "command-output",
											phase: payload.phase,
											title: payload.title,
											name: payload.name,
											status: payload.status,
											exitCode: payload.exitCode
										}));
									},
									onPatchSummary: async (payload) => {
										if (payload.phase !== "end") return;
										await pushStreamToolProgress(formatChannelProgressDraftLine({
											event: "patch",
											phase: payload.phase,
											title: payload.title,
											name: payload.name,
											added: payload.added,
											modified: payload.modified,
											deleted: payload.deleted,
											summary: payload.summary
										}));
									},
									onCompactionStart: statusReactionController ? async () => {
										await statusReactionController.setCompacting();
									} : void 0,
									onCompactionEnd: statusReactionController ? async () => {
										statusReactionController.cancelPending();
										await statusReactionController.setThinking();
									} : void 0,
									onModelSelected
								}
							});
						}
					})
				}
			});
			if (!turnResult.dispatched) return;
			({queuedFinal} = turnResult.dispatchResult);
			suppressSilentReplyFallback = turnResult.dispatchResult.sourceReplyDeliveryMode === "message_tool_only";
		} catch (err) {
			dispatchError = err;
			runtime.error?.(danger(`telegram dispatch failed: ${String(err)}`));
		} finally {
			progressDraft.cancel();
			await draftLaneEventQueue;
			nativeToolProgressDraft?.stop();
			await finalizeSkippedDuplicateAnswerBlockDraft();
			const lanesToCleanup = [{
				laneName: "answer",
				lane: answerLane
			}, {
				laneName: "reasoning",
				lane: reasoningLane
			}];
			for (const { lane } of lanesToCleanup) {
				const stream = lane.stream;
				if (!stream) continue;
				if (isDispatchSuperseded()) {
					await (typeof stream.discard === "function" ? stream.discard() : stream.stop());
					continue;
				}
				if (lane.finalized) await stream.stop();
				else await stream.clear();
			}
		}
	} finally {
		dispatchWasSuperseded = isDispatchSuperseded();
		releaseReplyFence();
		endTelegramInboundEventDeliveryCorrelation();
	}
	if (dispatchWasSuperseded) {
		if (statusReactionController) finalizeTelegramStatusReaction({
			outcome: "done",
			hasFinalResponse: true
		}).catch((err) => {
			logVerbose(`telegram: status reaction finalize failed: ${String(err)}`);
		});
		else removeAckReactionAfterReply({
			removeAfterReply: removeAckAfterReply,
			ackReactionPromise,
			ackReactionValue: ackReactionPromise ? "ack" : null,
			remove: () => (reactionApi?.(chatId, msg.message_id ?? 0, []) ?? Promise.resolve()).then(() => {}),
			onError: (err) => {
				if (!msg.message_id) return;
				logAckFailure({
					log: logVerbose,
					channel: "telegram",
					target: `${chatId}/${msg.message_id}`,
					error: err
				});
			}
		});
		if (!isRoomEvent || deliveryState.snapshot().delivered) clearGroupHistory();
		return;
	}
	let sentFallback = false;
	const deliverySummary = deliveryState.snapshot();
	if (!isRoomEvent && (dispatchError || !deliverySummary.delivered && (deliverySummary.skippedNonSilent > 0 || deliverySummary.failedNonSilent > 0))) {
		const fallbackText = dispatchError ? "Something went wrong while processing your request. Please try again." : EMPTY_RESPONSE_FALLBACK;
		sentFallback = (await (telegramDeps.deliverReplies ?? deliverReplies)({
			replies: [{ text: fallbackText }],
			...deliveryBaseOptions,
			silent: silentErrorReplies && (dispatchError != null || hadErrorReplyFailureOrSkip),
			mediaLoader: telegramDeps.loadWebMedia
		})).delivered;
	}
	if (!sentFallback && !dispatchError && !deliverySummary.delivered && !suppressSilentReplyFallback && !queuedFinal && isGroup) {
		const policySessionKey = ctxPayload.CommandSource === "native" ? ctxPayload.CommandTargetSessionKey ?? ctxPayload.SessionKey : ctxPayload.SessionKey;
		const silentReplyFallback = projectOutboundPayloadPlanForDelivery(createOutboundPayloadPlan([{ text: "NO_REPLY" }], {
			cfg,
			sessionKey: policySessionKey,
			surface: "telegram"
		}));
		if (silentReplyFallback.length > 0) sentFallback = (await (telegramDeps.deliverReplies ?? deliverReplies)({
			replies: silentReplyFallback,
			...deliveryBaseOptions,
			silent: false,
			mediaLoader: telegramDeps.loadWebMedia
		})).delivered;
		silentReplyDispatchLogger.debug("telegram turn ended without visible final response", {
			hasSessionKey: Boolean(policySessionKey),
			hasChatId: chatId != null,
			queuedFinal,
			sentFallback
		});
	}
	const hasFinalResponse = deliverySummary.delivered || sentFallback || suppressSilentReplyFallback || queuedFinal;
	if (statusReactionController && !hasFinalResponse) finalizeTelegramStatusReaction({
		outcome: "error",
		hasFinalResponse: false
	}).catch((err) => {
		logVerbose(`telegram: status reaction error finalize failed: ${String(err)}`);
	});
	const shouldClearGroupHistory = !isRoomEvent || deliverySummary.delivered || sentFallback || queuedFinal;
	if (!hasFinalResponse) {
		if (!shouldClearGroupHistory) return;
		clearGroupHistory();
		return;
	}
	if (isDmTopic && isFirstTurnInSession) {
		const userMessage = (ctxPayload.RawBody ?? ctxPayload.Body ?? "").slice(0, 500);
		if (userMessage.trim()) {
			const agentDir = resolveAgentDir(cfg, route.agentId);
			const directAutoTopicLabel = !isGroup && groupConfig && "autoTopicLabel" in groupConfig ? groupConfig.autoTopicLabel : void 0;
			const accountAutoTopicLabel = telegramCfg?.autoTopicLabel;
			const autoTopicConfig = resolveAutoTopicLabelConfig(directAutoTopicLabel, accountAutoTopicLabel);
			if (autoTopicConfig) {
				const topicThreadId = threadSpec.id;
				(async () => {
					try {
						const label = await generateTelegramTopicLabel({
							userMessage,
							prompt: autoTopicConfig.prompt,
							cfg,
							agentId: route.agentId,
							agentDir
						});
						if (!label) {
							logVerbose("auto-topic-label: LLM returned empty label");
							return;
						}
						logVerbose(`auto-topic-label: generated label (len=${label.length})`);
						await bot.api.editForumTopic(chatId, topicThreadId, { name: label });
						logVerbose(`auto-topic-label: renamed topic ${chatId}/${topicThreadId}`);
					} catch (err) {
						logVerbose(`auto-topic-label: failed: ${formatErrorMessage(err)}`);
					}
				})();
			}
		}
	}
	if (statusReactionController) finalizeTelegramStatusReaction({
		outcome: dispatchError || sentFallback ? "error" : "done",
		hasFinalResponse: true
	}).catch((err) => {
		logVerbose(`telegram: status reaction finalize failed: ${String(err)}`);
	});
	else removeAckReactionAfterReply({
		removeAfterReply: removeAckAfterReply,
		ackReactionPromise,
		ackReactionValue: ackReactionPromise ? "ack" : null,
		remove: () => (reactionApi?.(chatId, msg.message_id ?? 0, []) ?? Promise.resolve()).then(() => {}),
		onError: (err) => {
			if (!msg.message_id) return;
			logAckFailure({
				log: logVerbose,
				channel: "telegram",
				target: `${chatId}/${msg.message_id}`,
				error: err
			});
		}
	});
	if (shouldClearGroupHistory) clearGroupHistory();
};
//#endregion
//#region extensions/telegram/src/bot-message.ts
const telegramInboundLog = createSubsystemLogger("gateway/channels/telegram").child("inbound");
function formatTelegramInboundLogLine(params) {
	const kindLabel = params.mediaType ? `, ${params.mediaType}` : "";
	return `Inbound message ${params.from} -> ${params.to} (${params.chatType}${kindLabel}, ${params.body.length} chars)`;
}
const createTelegramMessageProcessor = (deps) => {
	const { bot, cfg, account, telegramCfg, historyLimit, groupHistories, dmPolicy, allowFrom, groupAllowFrom, ackReactionScope, logger, resolveGroupActivation, resolveGroupRequireMention, resolveTelegramGroupConfig, loadFreshConfig, sendChatActionHandler, runtime, replyToMode, streamMode, textLimit, telegramDeps, opts } = deps;
	const sessionRuntime = {
		...telegramDeps.buildChannelInboundEventContext ? { buildChannelInboundEventContext: telegramDeps.buildChannelInboundEventContext } : {},
		...telegramDeps.readSessionUpdatedAt ? { readSessionUpdatedAt: telegramDeps.readSessionUpdatedAt } : {},
		...telegramDeps.recordInboundSession ? { recordInboundSession: telegramDeps.recordInboundSession } : {},
		...telegramDeps.resolveInboundLastRouteSessionKey ? { resolveInboundLastRouteSessionKey: telegramDeps.resolveInboundLastRouteSessionKey } : {},
		...telegramDeps.resolvePinnedMainDmOwnerFromAllowlist ? { resolvePinnedMainDmOwnerFromAllowlist: telegramDeps.resolvePinnedMainDmOwnerFromAllowlist } : {},
		resolveStorePath: telegramDeps.resolveStorePath
	};
	const contextRuntime = telegramDeps.recordChannelActivity ? { recordChannelActivity: telegramDeps.recordChannelActivity } : void 0;
	return async (primaryCtx, allMedia, storeAllowFrom, options, replyMedia, replyChain, promptContext, lifecycle) => {
		const ingressReceivedAtMs = typeof options?.receivedAtMs === "number" && Number.isFinite(options.receivedAtMs) ? options.receivedAtMs : void 0;
		const ingressDebugEnabled = shouldLogVerbose() || process.env.OPENCLAW_DEBUG_TELEGRAM_INGRESS === "1";
		const ingressContextStartMs = ingressReceivedAtMs ? Date.now() : void 0;
		const context = await buildTelegramMessageContext({
			primaryCtx,
			allMedia,
			replyMedia,
			replyChain,
			promptContext,
			storeAllowFrom,
			options,
			bot,
			cfg,
			account,
			historyLimit,
			groupHistories,
			dmPolicy,
			allowFrom,
			groupAllowFrom,
			ackReactionScope,
			logger,
			resolveGroupActivation,
			resolveGroupRequireMention,
			resolveTelegramGroupConfig,
			sendChatActionHandler,
			loadFreshConfig,
			runtime: contextRuntime,
			sessionRuntime,
			upsertPairingRequest: telegramDeps.upsertChannelPairingRequest
		});
		if (!context) {
			if (ingressDebugEnabled && ingressReceivedAtMs && ingressContextStartMs) logVerbose(`telegram ingress: chatId=${primaryCtx.message.chat.id} dropped after ${Date.now() - ingressReceivedAtMs}ms` + (options?.ingressBuffer ? ` buffer=${options.ingressBuffer}` : ""));
			return false;
		}
		if (ingressDebugEnabled && ingressReceivedAtMs && ingressContextStartMs) logVerbose(`telegram ingress: chatId=${context.chatId} contextReadyMs=${Date.now() - ingressReceivedAtMs} preDispatchMs=${Date.now() - ingressContextStartMs}` + (options?.ingressBuffer ? ` buffer=${options.ingressBuffer}` : ""));
		if (context.ctxPayload.InboundEventKind !== "room_event" && context.initialTypingCueSent !== true) context.sendTyping().catch((err) => {
			logVerbose(`telegram early typing cue failed for chat ${context.chatId}: ${String(err)}`);
		});
		telegramInboundLog.info(formatTelegramInboundLogLine({
			from: context.ctxPayload.From,
			to: context.primaryCtx.me?.username ? `@${context.primaryCtx.me.username}` : context.ctxPayload.To,
			chatType: context.ctxPayload.ChatType,
			body: context.ctxPayload.RawBody,
			mediaType: allMedia[0]?.contentType
		}));
		await lifecycle?.onDispatchStart?.();
		try {
			await dispatchTelegramMessage({
				context,
				bot,
				cfg,
				runtime,
				replyToMode,
				streamMode,
				textLimit,
				telegramCfg,
				telegramDeps,
				opts
			});
			if (ingressDebugEnabled && ingressReceivedAtMs) logVerbose(`telegram ingress: chatId=${context.chatId} dispatchCompleteMs=${Date.now() - ingressReceivedAtMs}` + (options?.ingressBuffer ? ` buffer=${options.ingressBuffer}` : ""));
		} catch (err) {
			runtime.error?.(danger(`telegram message processing failed: ${String(err)}`));
			try {
				await bot.api.sendMessage(context.chatId, "Something went wrong while processing your request. Please try again.", buildTelegramThreadParams(context.threadSpec));
			} catch {}
		}
		return true;
	};
};
//#endregion
//#region extensions/telegram/src/bot-update-tracker.ts
function sortedIds(ids) {
	return [...ids].toSorted((a, b) => a - b);
}
function createTelegramUpdateTracker(options = {}) {
	const initialUpdateId = typeof options.initialUpdateId === "number" ? options.initialUpdateId : null;
	const persistenceFloorUpdateId = typeof options.persistenceFloorUpdateId === "number" ? options.persistenceFloorUpdateId : initialUpdateId;
	const ackPolicy = options.ackPolicy ?? "after_receive_record";
	const recentUpdates = createTelegramUpdateDedupe();
	const pendingUpdateKeys = /* @__PURE__ */ new Set();
	const activeHandledUpdateKeys = /* @__PURE__ */ new Map();
	const pendingUpdateIds = /* @__PURE__ */ new Set();
	const failedUpdateIds = /* @__PURE__ */ new Set();
	const completedFloorReplayUpdateIds = /* @__PURE__ */ new Set();
	let highestAcceptedUpdateId = initialUpdateId;
	let highestPersistedAcceptedUpdateId = persistenceFloorUpdateId;
	let highestPersistenceRequestedUpdateId = persistenceFloorUpdateId;
	let highestCompletedUpdateId = persistenceFloorUpdateId;
	let persistInFlight = false;
	let persistTargetUpdateId = null;
	const skip = (key) => {
		options.onSkip?.(key);
	};
	const drainPersistQueue = async () => {
		const persist = options.onAcceptedUpdateId;
		if (persistInFlight || typeof persist !== "function") return;
		persistInFlight = true;
		try {
			while (persistTargetUpdateId !== null) {
				const updateId = persistTargetUpdateId;
				persistTargetUpdateId = null;
				try {
					await persist(updateId);
					if (highestPersistedAcceptedUpdateId === null || updateId > highestPersistedAcceptedUpdateId) highestPersistedAcceptedUpdateId = updateId;
				} catch (err) {
					options.onPersistError?.(err);
				}
			}
		} finally {
			persistInFlight = false;
		}
	};
	const requestPersistAcceptedUpdateId = (updateId) => {
		if (typeof options.onAcceptedUpdateId !== "function") return;
		if (highestPersistenceRequestedUpdateId !== null && updateId <= highestPersistenceRequestedUpdateId) return;
		highestPersistenceRequestedUpdateId = updateId;
		persistTargetUpdateId = updateId;
		drainPersistQueue().catch((err) => {
			options.onPersistError?.(err);
		});
	};
	const acceptUpdateId = (updateId) => {
		if (highestAcceptedUpdateId !== null && updateId <= highestAcceptedUpdateId) return;
		highestAcceptedUpdateId = updateId;
	};
	const isFloorReplayUpdateId = (updateId) => initialUpdateId === null && persistenceFloorUpdateId !== null && updateId <= persistenceFloorUpdateId;
	function resolveSafeCompletedUpdateId() {
		if (highestCompletedUpdateId === null) return null;
		let safeCompletedUpdateId = highestCompletedUpdateId;
		for (const updateId of pendingUpdateIds) {
			if (persistenceFloorUpdateId !== null && updateId <= persistenceFloorUpdateId) continue;
			if (updateId <= safeCompletedUpdateId) safeCompletedUpdateId = updateId - 1;
		}
		for (const updateId of failedUpdateIds) {
			if (persistenceFloorUpdateId !== null && updateId <= persistenceFloorUpdateId) continue;
			if (updateId <= safeCompletedUpdateId) safeCompletedUpdateId = updateId - 1;
		}
		return safeCompletedUpdateId;
	}
	const persistUpdateIdAfterAck = async (updateId) => {
		const persistUpdateId = ackPolicy === "after_agent_dispatch" ? resolveSafeCompletedUpdateId() : updateId;
		if (persistUpdateId !== null) requestPersistAcceptedUpdateId(persistUpdateId);
	};
	const ackUpdateAfterStage = (receiveContext, stage) => {
		if (!receiveContext?.shouldAckAfter(stage)) return;
		receiveContext.ack().catch((err) => {
			options.onPersistError?.(err);
		});
	};
	const beginUpdate = (ctx) => {
		const updateId = resolveTelegramUpdateId(ctx);
		const updateKey = buildTelegramUpdateKey(ctx);
		if (typeof updateId === "number") if (highestAcceptedUpdateId !== null && updateId <= highestAcceptedUpdateId) {
			const floorReplay = isFloorReplayUpdateId(updateId);
			if (!floorReplay && !failedUpdateIds.has(updateId)) {
				skip(`update:${updateId}`);
				return {
					accepted: false,
					reason: "accepted-watermark"
				};
			}
			if (floorReplay && completedFloorReplayUpdateIds.has(updateId)) {
				skip(`update:${updateId}`);
				return {
					accepted: false,
					reason: "accepted-watermark"
				};
			}
		} else failedUpdateIds.delete(updateId);
		if (updateKey) {
			if (pendingUpdateKeys.has(updateKey) || recentUpdates.peek(updateKey)) {
				skip(updateKey);
				return {
					accepted: false,
					reason: "semantic-dedupe"
				};
			}
			pendingUpdateKeys.add(updateKey);
			activeHandledUpdateKeys.set(updateKey, false);
		}
		let receiveContext;
		if (typeof updateId === "number") {
			pendingUpdateIds.add(updateId);
			acceptUpdateId(updateId);
			receiveContext = createMessageReceiveContext({
				id: updateKey ?? `telegram:update:${updateId}`,
				channel: "telegram",
				message: ctx,
				ackPolicy,
				onAck: () => persistUpdateIdAfterAck(updateId)
			});
			ackUpdateAfterStage(receiveContext, "receive_record");
		}
		return {
			accepted: true,
			update: {
				...updateKey ? { key: updateKey } : {},
				...typeof updateId === "number" ? { updateId } : {},
				...receiveContext ? { receiveContext } : {}
			}
		};
	};
	const finishUpdate = (update, finish) => {
		if (update.key) {
			activeHandledUpdateKeys.delete(update.key);
			if (finish.completed) recentUpdates.check(update.key);
			pendingUpdateKeys.delete(update.key);
		}
		if (typeof update.updateId === "number") {
			pendingUpdateIds.delete(update.updateId);
			if (finish.completed) {
				failedUpdateIds.delete(update.updateId);
				if (isFloorReplayUpdateId(update.updateId)) completedFloorReplayUpdateIds.add(update.updateId);
				if (highestCompletedUpdateId === null || update.updateId > highestCompletedUpdateId) highestCompletedUpdateId = update.updateId;
				ackUpdateAfterStage(update.receiveContext, "agent_dispatch");
			} else {
				failedUpdateIds.add(update.updateId);
				update.receiveContext?.nack(/* @__PURE__ */ new Error("Telegram update handler did not complete")).catch((err) => {
					options.onPersistError?.(err);
				});
			}
		}
	};
	const shouldSkipHandlerDispatch = (ctx) => {
		const updateId = resolveTelegramUpdateId(ctx);
		if (typeof updateId === "number" && initialUpdateId !== null && updateId <= initialUpdateId) return true;
		const key = buildTelegramUpdateKey(ctx);
		if (!key) return false;
		const handled = activeHandledUpdateKeys.get(key);
		if (handled != null) {
			if (handled) {
				skip(key);
				return true;
			}
			activeHandledUpdateKeys.set(key, true);
			return false;
		}
		const skipped = recentUpdates.check(key);
		if (skipped) skip(key);
		return skipped;
	};
	const getState = () => ({
		highestAcceptedUpdateId,
		highestPersistedAcceptedUpdateId,
		highestCompletedUpdateId,
		safeCompletedUpdateId: resolveSafeCompletedUpdateId(),
		pendingUpdateIds: sortedIds(pendingUpdateIds),
		failedUpdateIds: sortedIds(failedUpdateIds)
	});
	return {
		beginUpdate,
		finishUpdate,
		getState,
		shouldSkipHandlerDispatch
	};
}
//#endregion
//#region extensions/telegram/src/raw-update-log.ts
const MAX_RAW_UPDATE_STRING = 500;
const MAX_RAW_UPDATE_ARRAY = 20;
const REDACTED_TELEGRAM_FIELD = "[redacted]";
const TELEGRAM_RAW_UPDATE_ALWAYS_REDACT_KEYS = new Set([
	"added_to_attachment_menu",
	"author_signature",
	"caption",
	"chat_instance",
	"data",
	"email",
	"bio",
	"description",
	"explanation",
	"file_id",
	"file_unique_id",
	"first_name",
	"invite_link",
	"is_premium",
	"language_code",
	"latitude",
	"last_name",
	"longitude",
	"name",
	"phone_number",
	"question",
	"query",
	"text",
	"title",
	"url",
	"username",
	"vcard"
]);
const TELEGRAM_RAW_UPDATE_ALLOWED_ID_KEYS = new Set(["message_id", "update_id"]);
const TELEGRAM_RAW_UPDATE_ID_REDACT_KEYS = new Set([
	"chat_id",
	"custom_emoji_id",
	"inline_message_id",
	"migrate_from_chat_id",
	"migrate_to_chat_id",
	"option_ids",
	"poll_id",
	"sender_chat_id",
	"user_id",
	"user_chat_id"
]);
function shouldRedactTelegramRawUpdateValue(key, parentKey) {
	if (!key) return false;
	if (TELEGRAM_RAW_UPDATE_ALWAYS_REDACT_KEYS.has(key)) return true;
	if (TELEGRAM_RAW_UPDATE_ALLOWED_ID_KEYS.has(key)) return false;
	if (TELEGRAM_RAW_UPDATE_ID_REDACT_KEYS.has(key)) return true;
	if (key === "id" || key.endsWith("_id") || key.endsWith("_ids")) return parentKey !== void 0;
	return false;
}
function isTelegramUserObject(value) {
	return typeof value.id === "number" && typeof value.is_bot === "boolean" && typeof value.first_name === "string";
}
function stringifyTelegramRawUpdateForLog(update) {
	const seen = /* @__PURE__ */ new WeakSet();
	const transform = (value, key = "", parentKey) => {
		if (shouldRedactTelegramRawUpdateValue(key, parentKey)) return REDACTED_TELEGRAM_FIELD;
		if (typeof value === "string") return value.length > MAX_RAW_UPDATE_STRING ? `${value.slice(0, MAX_RAW_UPDATE_STRING)}...` : value;
		if (Array.isArray(value)) {
			const items = value.slice(0, MAX_RAW_UPDATE_ARRAY).map((item) => transform(item, key, key));
			if (value.length > MAX_RAW_UPDATE_ARRAY) items.push(`...(${value.length - MAX_RAW_UPDATE_ARRAY} more)`);
			return items;
		}
		if (value && typeof value === "object") {
			if (seen.has(value)) return "[Circular]";
			seen.add(value);
			const record = value;
			if (isTelegramUserObject(record)) return REDACTED_TELEGRAM_FIELD;
			const redacted = {};
			for (const [entryKey, entryValue] of Object.entries(record)) redacted[entryKey] = transform(entryValue, entryKey, key);
			return redacted;
		}
		return value;
	};
	return JSON.stringify(transform(update ?? null));
}
//#endregion
//#region extensions/telegram/src/sendchataction-401-backoff.ts
const BACKOFF_POLICY = {
	initialMs: 1e3,
	maxMs: 3e5,
	factor: 2,
	jitter: .1
};
function is401Error(error) {
	if (!error) return false;
	const message = error instanceof Error ? error.message : JSON.stringify(error);
	return message.includes("401") || normalizeLowercaseStringOrEmpty(message).includes("unauthorized");
}
/**
* Creates a GLOBAL (per-account) handler for sendChatAction that tracks 401 errors
* across all message contexts. This prevents the infinite loop that caused Telegram
* to delete bots (issue #27092).
*
* When a 401 occurs, exponential backoff is applied (1s → 2s → 4s → ... → 5min).
* After maxConsecutive401 failures (default 10), all sendChatAction calls are
* suspended until reset() is called.
*/
function createTelegramSendChatActionHandler({ sendChatActionFn, logger, maxConsecutive401 = 10, minIntervalMs = 0, now = () => Date.now() }) {
	let consecutive401Failures = 0;
	let suspended = false;
	const blockedUntilByKey = /* @__PURE__ */ new Map();
	const reset = () => {
		consecutive401Failures = 0;
		suspended = false;
		blockedUntilByKey.clear();
	};
	const sendChatAction = async (chatId, action, threadParams) => {
		if (suspended) return;
		const key = minIntervalMs > 0 ? `${String(chatId)}:${action}` : void 0;
		const attemptedAt = key ? now() : 0;
		if (key) {
			const blockedUntil = blockedUntilByKey.get(key);
			if (blockedUntil !== void 0 && attemptedAt < blockedUntil) return;
			blockedUntilByKey.set(key, Number.POSITIVE_INFINITY);
		}
		if (consecutive401Failures > 0) {
			const backoffMs = computeBackoff(BACKOFF_POLICY, consecutive401Failures);
			logger(`sendChatAction backoff: waiting ${backoffMs}ms before retry (failure ${consecutive401Failures}/${maxConsecutive401})`);
			await sleepWithAbort(backoffMs);
		}
		try {
			await sendChatActionFn(chatId, action, threadParams);
			if (consecutive401Failures > 0) {
				logger(`sendChatAction recovered after ${consecutive401Failures} consecutive 401 failures`);
				consecutive401Failures = 0;
			}
		} catch (error) {
			if (is401Error(error)) {
				consecutive401Failures++;
				if (consecutive401Failures >= maxConsecutive401) {
					suspended = true;
					logger(`CRITICAL: sendChatAction suspended after ${consecutive401Failures} consecutive 401 errors. Bot token is likely invalid. Telegram may DELETE the bot if requests continue. Replace the token and restart: openclaw channels restart telegram`);
				} else logger(`sendChatAction 401 error (${consecutive401Failures}/${maxConsecutive401}). Retrying with exponential backoff.`);
			}
			throw error;
		} finally {
			if (key) blockedUntilByKey.set(key, attemptedAt + minIntervalMs);
		}
	};
	return {
		sendChatAction,
		isSuspended: () => suspended,
		reset
	};
}
//#endregion
//#region extensions/telegram/src/bot-core.ts
function resolveTelegramScopedGroupConfig(telegramCfg, chatId, messageThreadId) {
	const resolveTopicConfig = (scopedConfig) => {
		if (!scopedConfig || messageThreadId == null) return;
		const defaultConfig = scopedConfig.topics?.["*"];
		const exactConfig = scopedConfig.topics?.[String(messageThreadId)];
		if (defaultConfig && exactConfig) return {
			...defaultConfig,
			...exactConfig
		};
		return exactConfig ?? defaultConfig;
	};
	const groups = telegramCfg.groups;
	const direct = telegramCfg.direct;
	const chatIdStr = String(chatId);
	if (!chatIdStr.startsWith("-")) {
		const groupConfig = direct?.[chatIdStr] ?? direct?.["*"];
		return {
			groupConfig,
			topicConfig: resolveTopicConfig(groupConfig)
		};
	}
	const groupConfig = groups?.[chatIdStr] ?? groups?.["*"];
	return {
		groupConfig,
		topicConfig: resolveTopicConfig(groupConfig)
	};
}
const DEFAULT_TELEGRAM_BOT_RUNTIME = {
	Bot,
	sequentialize,
	apiThrottler
};
const TELEGRAM_TYPING_COALESCE_MS = 4e3;
function createTelegramBotCore(opts) {
	const botRuntime = DEFAULT_TELEGRAM_BOT_RUNTIME;
	const runtime = opts.runtime ?? createNonExitingRuntime();
	const telegramDeps = opts.telegramDeps;
	const cfg = opts.config ?? telegramDeps.getRuntimeConfig();
	const account = resolveTelegramAccount({
		cfg,
		accountId: opts.accountId
	});
	const threadBindingManager = resolveThreadBindingSpawnPolicy({
		cfg,
		channel: "telegram",
		accountId: account.accountId,
		kind: "subagent"
	}).enabled ? createTelegramThreadBindingManager({
		cfg,
		accountId: account.accountId,
		idleTimeoutMs: resolveThreadBindingIdleTimeoutMsForChannel({
			cfg,
			channel: "telegram",
			accountId: account.accountId
		}),
		maxAgeMs: resolveThreadBindingMaxAgeMsForChannel({
			cfg,
			channel: "telegram",
			accountId: account.accountId
		})
	}) : null;
	const telegramCfg = account.config;
	const telegramTransport = opts.telegramTransport ?? resolveTelegramTransport(opts.proxyFetch, { network: telegramCfg.network });
	const finalFetch = createTelegramClientFetch({
		fetchImpl: asTelegramClientFetch(telegramTransport.fetch),
		timeoutSeconds: telegramCfg?.timeoutSeconds,
		shutdownSignal: opts.fetchAbortSignal,
		transport: telegramTransport
	});
	const timeoutSeconds = resolveTelegramClientTimeoutSeconds({
		value: telegramCfg?.timeoutSeconds,
		minimum: resolveTelegramClientTimeoutMinimumSeconds([opts.minimumClientTimeoutSeconds, resolveTelegramOutboundClientTimeoutFloorSeconds(telegramCfg?.timeoutSeconds)])
	});
	const apiRoot = normalizeOptionalString(telegramCfg.apiRoot);
	const normalizedApiRoot = apiRoot ? normalizeTelegramApiRoot(apiRoot) : void 0;
	const client = finalFetch || timeoutSeconds || normalizedApiRoot ? {
		...finalFetch ? { fetch: asTelegramClientFetch(finalFetch) } : {},
		...timeoutSeconds ? { timeoutSeconds } : {},
		...normalizedApiRoot ? { apiRoot: normalizedApiRoot } : {}
	} : void 0;
	const botConfig = client || opts.botInfo ? {
		...client ? { client } : {},
		...opts.botInfo ? { botInfo: opts.botInfo } : {}
	} : void 0;
	const bot = new botRuntime.Bot(opts.token, botConfig);
	bot.api.config.use(getOrCreateAccountThrottler(opts.token, botRuntime.apiThrottler));
	bot.catch((err) => {
		runtime.error?.(danger(`telegram bot error: ${formatUncaughtError(err)}`));
	});
	const initialUpdateId = typeof opts.updateOffset?.lastUpdateId === "number" ? opts.updateOffset.lastUpdateId : null;
	const logSkippedUpdate = (key) => {
		if (shouldLogVerbose()) logVerbose(`telegram dedupe: skipped ${key}`);
	};
	const updateTracker = createTelegramUpdateTracker({
		initialUpdateId,
		persistenceFloorUpdateId: typeof opts.updateOffset?.persistenceFloorUpdateId === "number" ? opts.updateOffset.persistenceFloorUpdateId : initialUpdateId,
		ackPolicy: "after_agent_dispatch",
		...typeof opts.updateOffset?.onUpdateId === "function" ? { onAcceptedUpdateId: opts.updateOffset.onUpdateId } : {},
		onPersistError: (err) => {
			runtime.error?.(`telegram: failed to persist update watermark: ${formatErrorMessage(err)}`);
		},
		onSkip: logSkippedUpdate
	});
	const shouldSkipUpdate = (ctx) => updateTracker.shouldSkipHandlerDispatch(ctx);
	bot.use(async (ctx, next) => {
		const begin = updateTracker.beginUpdate(ctx);
		if (!begin.accepted) return;
		try {
			await next();
			updateTracker.finishUpdate(begin.update, { completed: true });
		} catch (error) {
			updateTracker.finishUpdate(begin.update, { completed: false });
			throw error;
		}
	});
	bot.use(botRuntime.sequentialize(getTelegramSequentialKey));
	const rawUpdateLogger = createSubsystemLogger("gateway/channels/telegram/raw-update");
	const MAX_RAW_UPDATE_CHARS = 8e3;
	bot.use(async (ctx, next) => {
		if (shouldLogVerbose()) try {
			const raw = stringifyTelegramRawUpdateForLog(ctx.update);
			const preview = raw.length > MAX_RAW_UPDATE_CHARS ? `${raw.slice(0, MAX_RAW_UPDATE_CHARS)}...` : raw;
			rawUpdateLogger.debug(`telegram update: ${preview}`);
		} catch (err) {
			rawUpdateLogger.debug(`telegram update log failed: ${String(err)}`);
		}
		await next();
	});
	const historyLimit = Math.max(0, telegramCfg.historyLimit ?? cfg.messages?.groupChat?.historyLimit ?? 50);
	const groupHistories = /* @__PURE__ */ new Map();
	const textLimit = resolveTextChunkLimit(cfg, "telegram", account.accountId);
	const dmPolicy = telegramCfg.dmPolicy ?? "pairing";
	const allowFrom = opts.allowFrom ?? telegramCfg.allowFrom;
	const groupAllowFrom = opts.groupAllowFrom ?? telegramCfg.groupAllowFrom ?? telegramCfg.allowFrom ?? allowFrom;
	const replyToMode = opts.replyToMode ?? telegramCfg.replyToMode ?? "off";
	const nativeEnabled = resolveNativeCommandsEnabled({
		providerId: "telegram",
		providerSetting: telegramCfg.commands?.native,
		globalSetting: cfg.commands?.native
	});
	const nativeSkillsEnabled = resolveNativeSkillsEnabled({
		providerId: "telegram",
		providerSetting: telegramCfg.commands?.nativeSkills,
		globalSetting: cfg.commands?.nativeSkills
	});
	const nativeDisabledExplicit = isNativeCommandsExplicitlyDisabled({
		providerSetting: telegramCfg.commands?.native,
		globalSetting: cfg.commands?.native
	});
	const useAccessGroups = cfg.commands?.useAccessGroups !== false;
	const ackReactionScope = cfg.messages?.ackReactionScope ?? "group-mentions";
	const mediaMaxBytes = (opts.mediaMaxMb ?? telegramCfg.mediaMaxMb ?? 100) * 1024 * 1024;
	const logger = getChildLogger({ module: "telegram-auto-reply" });
	const streamMode = resolveTelegramStreamMode(telegramCfg);
	const resolveGroupPolicy = (chatId) => resolveChannelGroupPolicy({
		cfg,
		channel: "telegram",
		accountId: account.accountId,
		groupId: String(chatId)
	});
	const resolveGroupActivation = (params) => {
		const agentId = params.agentId ?? resolveDefaultAgentId(cfg);
		const sessionKey = params.sessionKey ?? `agent:${agentId}:telegram:group:${buildTelegramGroupPeerId(params.chatId, params.messageThreadId)}`;
		const storePath = telegramDeps.resolveStorePath(cfg.session?.store, { agentId });
		try {
			const loadSessionStore = telegramDeps.loadSessionStore;
			if (!loadSessionStore) return;
			const entry = loadSessionStore(storePath)[sessionKey];
			if (entry?.groupActivation === "always") return false;
			if (entry?.groupActivation === "mention") return true;
		} catch (err) {
			logVerbose(`Failed to load session for activation check: ${String(err)}`);
		}
	};
	const resolveGroupRequireMention = (chatId) => resolveChannelGroupRequireMention({
		cfg,
		channel: "telegram",
		accountId: account.accountId,
		groupId: String(chatId),
		requireMentionOverride: opts.requireMention,
		overrideOrder: "after-config"
	});
	const loadFreshTelegramAccountConfig = () => {
		try {
			return resolveTelegramAccount({
				cfg: telegramDeps.getRuntimeConfig(),
				accountId: account.accountId
			}).config;
		} catch (error) {
			logVerbose(`telegram: failed to load fresh config for account ${account.accountId}; using startup snapshot: ${String(error)}`);
			return telegramCfg;
		}
	};
	const resolveTelegramGroupConfig = (chatId, messageThreadId) => {
		return resolveTelegramScopedGroupConfig(loadFreshTelegramAccountConfig(), chatId, messageThreadId);
	};
	const processMessage = createTelegramMessageProcessor({
		bot,
		cfg,
		account,
		telegramCfg,
		historyLimit,
		groupHistories,
		dmPolicy,
		allowFrom,
		groupAllowFrom,
		ackReactionScope,
		logger,
		resolveGroupActivation,
		resolveGroupRequireMention,
		resolveTelegramGroupConfig,
		loadFreshConfig: () => telegramDeps.getRuntimeConfig(),
		sendChatActionHandler: createTelegramSendChatActionHandler({
			sendChatActionFn: (chatId, action, threadParams) => bot.api.sendChatAction(chatId, action, threadParams),
			logger: (message) => logVerbose(`telegram: ${message}`),
			minIntervalMs: TELEGRAM_TYPING_COALESCE_MS
		}),
		runtime,
		replyToMode,
		streamMode,
		textLimit,
		opts,
		telegramDeps
	});
	registerTelegramNativeCommands({
		bot,
		cfg,
		runtime,
		accountId: account.accountId,
		telegramCfg,
		allowFrom,
		groupAllowFrom,
		replyToMode,
		textLimit,
		mediaMaxBytes,
		useAccessGroups,
		nativeEnabled,
		nativeSkillsEnabled,
		nativeDisabledExplicit,
		resolveGroupPolicy,
		resolveTelegramGroupConfig,
		shouldSkipUpdate,
		opts,
		telegramDeps
	});
	registerTelegramHandlers({
		cfg,
		accountId: account.accountId,
		bot,
		opts,
		telegramTransport,
		runtime,
		mediaMaxBytes,
		telegramCfg,
		allowFrom,
		groupAllowFrom,
		resolveGroupPolicy,
		resolveGroupActivation,
		resolveGroupRequireMention,
		resolveTelegramGroupConfig,
		shouldSkipUpdate,
		processMessage,
		logger,
		telegramDeps
	});
	const originalStop = bot.stop.bind(bot);
	bot.stop = ((...args) => {
		threadBindingManager?.stop();
		return originalStop(...args);
	});
	return bot;
}
//#endregion
//#region extensions/telegram/src/bot.ts
function createTelegramBot(opts) {
	return createTelegramBotCore({
		...opts,
		telegramDeps: opts.telegramDeps ?? defaultTelegramBotDeps
	});
}
//#endregion
export { getTelegramSequentialKey as i, buildTelegramReplyFenceLaneKey as n, supersedeTelegramReplyFenceLane as r, createTelegramBot as t };