fix(mattermost): truncate inbound preview on code-point boundary (#101630)

Co-authored-by: NIO <nocodet@mail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
NIO 2026-07-09 11:05:03 +08:00 committed by GitHub
parent 1825c9f890
commit f77c2aafa0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 52 additions and 3 deletions

View file

@ -159,6 +159,7 @@ function createRuntimeCore(
shouldHandleTextCommands?: () => boolean;
textHasControlCommand?: (text?: string) => boolean;
createInboundDebouncer?: typeof createInboundDebouncer;
verboseDebug?: (message: string) => void;
} = {},
) {
const dispatchPreparedForTest = vi.fn(
@ -223,9 +224,9 @@ function createRuntimeCore(
current: () => cfg,
},
logging: {
shouldLogVerbose: () => false,
shouldLogVerbose: () => Boolean(overrides.verboseDebug),
getChildLogger: () => ({
debug: vi.fn(),
debug: overrides.verboseDebug ?? vi.fn(),
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
@ -447,6 +448,53 @@ describe("mattermost inbound user posts", () => {
expect(ctx?.Provider).toBe("mattermost");
});
it("keeps verbose inbound previews on complete UTF-16 boundaries", async () => {
const socket = new FakeWebSocket();
const abortController = new AbortController();
const verboseDebug = vi.fn();
mockState.abortController = abortController;
mockState.runtimeCore = createRuntimeCore(testConfig, undefined, { verboseDebug });
const monitor = monitorMattermostProvider({
config: testConfig,
runtime: testRuntime(),
abortSignal: abortController.signal,
webSocketFactory: () => socket,
});
await vi.waitFor(() => {
expect(socket.openListenerCount).toBeGreaterThan(0);
});
socket.emitOpen();
await socket.emitMessage({
event: "posted",
data: {
channel_id: "chan-1",
channel_name: "town-square",
channel_display_name: "Town Square",
sender_name: "alice",
post: JSON.stringify({
id: "post-verbose-preview",
channel_id: "chan-1",
user_id: "user-1",
message: `${"a".repeat(199)}😀tail`,
create_at: 1_714_000_000_000,
}),
},
broadcast: {
channel_id: "chan-1",
user_id: "user-1",
},
});
socket.emitClose(1000);
await monitor;
expect(verboseDebug).toHaveBeenCalledWith(
`mattermost inbound: from=mattermost:channel:chan-1 len=205 preview="${"a".repeat(199)}"`,
);
});
it("dispatches a bare bot mention whose body is empty after normalization as a wake event", async () => {
const socket = new FakeWebSocket();
const abortController = new AbortController();

View file

@ -24,6 +24,7 @@ import {
normalizeTrimmedStringList,
uniqueStrings,
} from "openclaw/plugin-sdk/string-coerce-runtime";
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
import { getMattermostRuntime } from "../runtime.js";
import {
resolveMattermostAccount,
@ -1677,7 +1678,7 @@ export async function monitorMattermostProvider(opts: MonitorMattermostOpts = {}
agentId: route.agentId,
});
const previewLine = bodyText.slice(0, 200).replace(/\n/g, "\\n");
const previewLine = truncateUtf16Safe(bodyText, 200).replace(/\n/g, "\\n");
logVerboseMessage(
`mattermost inbound: from=${ctxPayload.From} len=${bodyText.length} preview="${previewLine}"`,
);