diff --git a/extensions/telegram/src/bot-native-commands.ts b/extensions/telegram/src/bot-native-commands.ts index 5ddd86066def..0a13b63eccf6 100644 --- a/extensions/telegram/src/bot-native-commands.ts +++ b/extensions/telegram/src/bot-native-commands.ts @@ -31,10 +31,7 @@ import type { TelegramTopicConfig, } from "openclaw/plugin-sdk/config-contracts"; import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/markdown-table-runtime"; -import { - resolveSendableOutboundReplyParts, - type ReplyPayload, -} from "openclaw/plugin-sdk/reply-payload"; +import { resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload"; import { resolveAgentRoute } from "openclaw/plugin-sdk/routing"; import { getRuntimeConfigSnapshot } from "openclaw/plugin-sdk/runtime-config-snapshot"; import { danger, logVerbose } from "openclaw/plugin-sdk/runtime-env"; @@ -1311,11 +1308,12 @@ export const registerTelegramNativeCommands = ({ ); if (isDirectStatusNotice) { + const replyToId = String(msg.message_id); + const repliesForDelivery = directReplies.map((r) => + r.replyToId ? r : Object.assign({}, r, { replyToId }), + ); const result = await deliverReplies({ - replies: directReplies.map((r) => ({ - ...r, - replyToId: r.replyToId ?? String(msg.message_id), - })), + replies: repliesForDelivery, ...deliveryBaseOptions, silent: false, }); diff --git a/extensions/telegram/src/bot.create-telegram-bot.test-harness.ts b/extensions/telegram/src/bot.create-telegram-bot.test-harness.ts index 17c9869f414d..ac1a46c7418c 100644 --- a/extensions/telegram/src/bot.create-telegram-bot.test-harness.ts +++ b/extensions/telegram/src/bot.create-telegram-bot.test-harness.ts @@ -33,6 +33,7 @@ type ReplyPayloadLike = { mediaUrl?: string; mediaUrls?: string[]; replyToId?: string; + isStatusNotice?: boolean; }; const { sessionStorePath } = vi.hoisted(() => { diff --git a/extensions/telegram/src/bot.create-telegram-bot.test.ts b/extensions/telegram/src/bot.create-telegram-bot.test.ts index 58382a068250..b04310734f7b 100644 --- a/extensions/telegram/src/bot.create-telegram-bot.test.ts +++ b/extensions/telegram/src/bot.create-telegram-bot.test.ts @@ -3843,7 +3843,7 @@ describe("createTelegramBot", () => { expect(settings.groupSystemPrompt).toBe("Group prompt\n\nTopic prompt"); expect(settings.skillFilter).toStrictEqual([]); }); - it("delivers native /compact ack when buffered dispatch drops final delivery", async () => { + it("delivers native /compact status notice directly without dispatch pipeline", async () => { commandSpy.mockClear(); sendMessageSpy.mockClear(); dispatchReplyWithBufferedBlockDispatcher.mockClear(); @@ -3851,17 +3851,6 @@ describe("createTelegramBot", () => { text: "⚙️ Compaction skipped: already_compacted_recently • ctx 0%", isStatusNotice: true, }); - dispatchReplyWithBufferedBlockDispatcher.mockImplementation(async (params) => { - if (params.replyResolver) { - await params.replyResolver(params.ctx, params.replyOptions); - } else { - await replySpy(params.ctx, params.replyOptions); - } - return { - queuedFinal: false, - counts: { block: 0, final: 1, tool: 0 }, - }; - }); loadConfig.mockReturnValue({ commands: { native: true }, @@ -3896,6 +3885,7 @@ describe("createTelegramBot", () => { expect(sendMessageSpy).toHaveBeenCalled(); const compactReply = requireValue(sendMessageSpy.mock.calls.at(0), "compact reply call"); expect(String(compactReply[1])).toContain("Compaction skipped"); + expect(dispatchReplyWithBufferedBlockDispatcher).not.toHaveBeenCalled(); }); it("threads native command replies inside topics", async () => { diff --git a/extensions/telegram/src/native-command-ack-fallback.test.ts b/extensions/telegram/src/native-command-ack-fallback.test.ts deleted file mode 100644 index 00f783b20fd2..000000000000 --- a/extensions/telegram/src/native-command-ack-fallback.test.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; -import { - deliverNativeCommandAckFallback, - shouldDeliverNativeCommandAckFallback, -} from "./native-command-ack-fallback.js"; - -describe("native-command-ack-fallback", () => { - it("detects status-notice command ack payloads", () => { - expect( - shouldDeliverNativeCommandAckFallback({ - text: "⚙️ Compaction skipped: already_compacted_recently • ctx 0%", - isStatusNotice: true, - }), - ).toBe(true); - expect(shouldDeliverNativeCommandAckFallback({ text: "hello" })).toBe(false); - }); - - it("delivers captured command ack when primary dispatch did not", async () => { - const deliverReplies = vi.fn(async () => ({ delivered: true })); - const delivered = await deliverNativeCommandAckFallback({ - reply: { - text: "⚙️ Compaction skipped: already_compacted_recently • ctx 0%", - isStatusNotice: true, - }, - delivered: false, - replyToMessageId: "42", - deliverReplies, - }); - expect(delivered).toBe(true); - expect(deliverReplies).toHaveBeenCalledWith({ - replies: [ - { - text: "⚙️ Compaction skipped: already_compacted_recently • ctx 0%", - isStatusNotice: true, - replyToId: "42", - }, - ], - }); - }); -}); diff --git a/extensions/telegram/src/native-command-ack-fallback.ts b/extensions/telegram/src/native-command-ack-fallback.ts deleted file mode 100644 index f0a0dd0ffa4d..000000000000 --- a/extensions/telegram/src/native-command-ack-fallback.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload"; -import type { ReplyPayload } from "openclaw/plugin-sdk/reply-payload"; - -export function shouldDeliverNativeCommandAckFallback(reply: ReplyPayload): boolean { - const { trimmedText } = resolveSendableOutboundReplyParts(reply); - if (!trimmedText) { - return false; - } - // Command handlers (/compact, /status, /stop, …) emit user-initiated system feedback. - return reply.isStatusNotice === true || trimmedText.startsWith("⚙️"); -} - -export async function deliverNativeCommandAckFallback(params: { - reply: ReplyPayload | ReplyPayload[] | undefined; - delivered: boolean; - deliverReplies: (params: { replies: ReplyPayload[] }) => Promise<{ delivered: boolean }>; - replyToMessageId: string; -}): Promise { - if (params.delivered || !params.reply) { - return params.delivered; - } - const replies = Array.isArray(params.reply) ? params.reply : [params.reply]; - for (const original of replies) { - if (!shouldDeliverNativeCommandAckFallback(original)) { - continue; - } - const result = await params.deliverReplies({ - replies: [ - original.replyToId - ? original - : { - ...original, - replyToId: params.replyToMessageId, - }, - ], - }); - if (result.delivered) { - return true; - } - } - return false; -} diff --git a/src/auto-reply/reply/commands-compact.ts b/src/auto-reply/reply/commands-compact.ts index 2bd4ed8864d5..836c89ef80dd 100644 --- a/src/auto-reply/reply/commands-compact.ts +++ b/src/auto-reply/reply/commands-compact.ts @@ -2,7 +2,6 @@ import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id"; import { normalizeLowercaseStringOrEmpty, - normalizeOptionalLowercaseString, normalizeOptionalString, } from "@openclaw/normalization-core/string-coerce"; import { resolveAgentDir, resolveSessionAgentId } from "../../agents/agent-scope.js";