From 4bd68aef657e98e0bc2ae981b19485b10474fdf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E6=B5=A9=E5=AE=870668001029?= Date: Thu, 25 Jun 2026 00:39:09 +0800 Subject: [PATCH] fix(telegram): keep draft preview chunks surrogate-safe --- extensions/telegram/src/draft-stream.test.ts | 14 ++++++++++++++ extensions/telegram/src/draft-stream.ts | 3 ++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/extensions/telegram/src/draft-stream.test.ts b/extensions/telegram/src/draft-stream.test.ts index fc1a0b65a950..412ecf12a910 100644 --- a/extensions/telegram/src/draft-stream.test.ts +++ b/extensions/telegram/src/draft-stream.test.ts @@ -819,6 +819,20 @@ describe("createTelegramDraftStream", () => { expect(stream.lastDeliveredText?.()).toBe("1234567890"); }); + it("does not split surrogate pairs when clamping a first oversized non-final preview", async () => { + const api = createMockDraftApi(); + const stream = createDraftStream(api, { maxChars: 10 }); + + stream.update("123456789😀tail"); + await stream.flush(); + + expect(api.sendMessage).toHaveBeenCalledTimes(1); + const sentText = requireSendMessageCallText(api, 0); + expect(sentText).toBe("123456789"); + expect(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])/.test(sentText)).toBe(false); + expect(stream.lastDeliveredText?.()).toBe("123456789"); + }); + it("finalizes overflow that was hidden by a clamped non-final preview", async () => { const api = createMockDraftApi(); api.sendMessage diff --git a/extensions/telegram/src/draft-stream.ts b/extensions/telegram/src/draft-stream.ts index 578e4171b708..ff16a4224c3f 100644 --- a/extensions/telegram/src/draft-stream.ts +++ b/extensions/telegram/src/draft-stream.ts @@ -5,6 +5,7 @@ import { takeMessageIdAfterStop, } from "openclaw/plugin-sdk/channel-outbound"; import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; +import { sliceUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime"; import { buildTelegramThreadParams, type TelegramThreadSpec } from "./bot/helpers.js"; import { renderTelegramHtmlText, telegramHtmlToPlainTextFallback } from "./format.js"; import { @@ -169,7 +170,7 @@ function findTelegramDraftChunkLength( high = mid - 1; } } - return best; + return sliceUtf16Safe(text, 0, best).length; } export function createTelegramDraftStream(params: {