fix(telegram): stop block streaming from splitting messages when streamMode is off (#17704)

Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 847162caad
Co-authored-by: saivarunk <2976867+saivarunk@users.noreply.github.com>
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Reviewed-by: @obviyus
This commit is contained in:
Varun Kruthiventi
2026-02-16 09:57:29 +05:30
committed by GitHub
parent 1b223dbdd8
commit c62b90a2b7
3 changed files with 26 additions and 2 deletions

View File

@@ -48,6 +48,7 @@ Docs: https://docs.openclaw.ai
- Telegram: replace inbound `<media:audio>` placeholder with successful preflight voice transcript in message body context, preventing placeholder-only prompt bodies for mention-gated voice messages. (#16789) Thanks @Limitless2023.
- Telegram: retry inbound media `getFile` calls (3 attempts with backoff) and gracefully fall back to placeholder-only processing when retries fail, preventing dropped voice/media messages on transient Telegram network errors. (#16154) Thanks @yinghaosang.
- Telegram: finalize streaming preview replies in place instead of sending a second final message, preventing duplicate Telegram assistant outputs at stream completion. (#17218) Thanks @obviyus.
- Telegram: disable block streaming when `channels.telegram.streamMode` is `off`, preventing newline/content-block replies from splitting into multiple messages. (#17679) Thanks @saivarunk.
- Discord: preserve channel session continuity when runtime payloads omit `message.channelId` by falling back to event/raw `channel_id` values for routing/session keys, so same-channel messages keep history across turns/restarts. Also align diagnostics so active Discord runs no longer appear as `sessionKey=unknown`. (#17622) Thanks @shakkernerd.
- Discord: dedupe native skill commands by skill name in multi-agent setups to prevent duplicated slash commands with `_2` suffixes. (#17365) Thanks @seewhyme.
- Discord: ensure role allowlist matching uses raw role IDs for message routing authorization. Thanks @xinhuagu.

View File

@@ -113,6 +113,7 @@ describe("dispatchTelegramMessage draft streaming", () => {
async function dispatchWithContext(params: {
context: TelegramMessageContext;
telegramCfg?: Parameters<typeof dispatchTelegramMessage>[0]["telegramCfg"];
streamMode?: Parameters<typeof dispatchTelegramMessage>[0]["streamMode"];
}) {
await dispatchTelegramMessage({
context: params.context,
@@ -120,7 +121,7 @@ describe("dispatchTelegramMessage draft streaming", () => {
cfg: {},
runtime: createRuntime(),
replyToMode: "first",
streamMode: "partial",
streamMode: params.streamMode ?? "partial",
textLimit: 4096,
telegramCfg: params.telegramCfg ?? {},
opts: { token: "token" },
@@ -236,4 +237,26 @@ describe("dispatchTelegramMessage draft streaming", () => {
expect(draftStream.clear).toHaveBeenCalledTimes(1);
expect(draftStream.stop).toHaveBeenCalled();
});
it("disables block streaming when streamMode is off", async () => {
dispatchReplyWithBufferedBlockDispatcher.mockImplementation(async ({ dispatcherOptions }) => {
await dispatcherOptions.deliver({ text: "Hello" }, { kind: "final" });
return { queuedFinal: true };
});
deliverReplies.mockResolvedValue({ delivered: true });
await dispatchWithContext({
context: createContext(),
streamMode: "off",
});
expect(createTelegramDraftStream).not.toHaveBeenCalled();
expect(dispatchReplyWithBufferedBlockDispatcher).toHaveBeenCalledWith(
expect.objectContaining({
replyOptions: expect.objectContaining({
disableBlockStreaming: true,
}),
}),
);
});
});

View File

@@ -169,7 +169,7 @@ export const dispatchTelegramMessage = async ({
const disableBlockStreaming =
typeof telegramCfg.blockStreaming === "boolean"
? !telegramCfg.blockStreaming
: draftStream
: draftStream || streamMode === "off"
? true
: undefined;