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: {