Skip to content

Commit a9b3c61

Browse files
NIOcursoragent
andcommitted
fix(slack): keep inbound message preview truncation UTF-16 safe
Replace raw .slice(0, 160) inbound preview truncation with truncateUtf16Safe so emoji straddling the cap are dropped whole instead of leaving unpaired surrogates in Slack inbound message previews. Co-authored-by: Cursor <[email protected]>
1 parent 22376d8 commit a9b3c61

2 files changed

Lines changed: 11 additions & 1 deletion

File tree

extensions/slack/src/monitor/message-handler/prepare.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,15 @@ describe("slack prepareSlackMessage inbound contract", () => {
187187
expect(prepared.ctxPayload.BodyForAgent).toContain(body);
188188
});
189189

190+
it("truncates inbound preview on a surrogate boundary", async () => {
191+
const body = `${"a".repeat(159)}🐱`;
192+
const prepared = await prepareWithDefaultCtx(createSlackMessage({ text: body }));
193+
194+
assertPrepared(prepared);
195+
expect(prepared.preview).toBe("a".repeat(159));
196+
expect(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])/.test(prepared.preview)).toBe(false);
197+
});
198+
190199
it("logs inbound metadata without logging message content", async () => {
191200
const body = "confidential acquisition target: northstar; do not include this text in logs";
192201
shouldLogVerboseMock.mockReturnValue(true);

extensions/slack/src/monitor/message-handler/prepare.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import {
3535
normalizeOptionalString,
3636
} from "openclaw/plugin-sdk/string-coerce-runtime";
3737
import { enqueueSystemEvent } from "openclaw/plugin-sdk/system-event-runtime";
38+
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
3839
import { resolveSlackReplyToMode } from "../../account-reply-mode.js";
3940
import type { ResolvedSlackAccount } from "../../accounts.js";
4041
import { reactSlackMessage } from "../../actions.js";
@@ -1178,7 +1179,7 @@ export async function prepareSlackMessage(params: {
11781179

11791180
const roomLabel = channelName ? `#${channelName}` : `#${message.channel}`;
11801181
const senderName = await resolveSenderName();
1181-
const preview = rawBody.replace(/\s+/g, " ").slice(0, 160);
1182+
const preview = truncateUtf16Safe(rawBody.replace(/\s+/g, " "), 160);
11821183
const inboundLabel = isDirectMessage
11831184
? `Slack DM from ${senderName}`
11841185
: `Slack message in ${roomLabel} from ${senderName}`;

0 commit comments

Comments
 (0)