Skip to content

Commit f77c2aa

Browse files
hugenshenNIOcursoragent
authored
fix(mattermost): truncate inbound preview on code-point boundary (#101630)
Co-authored-by: NIO <[email protected]> Co-authored-by: Cursor <[email protected]>
1 parent 1825c9f commit f77c2aa

2 files changed

Lines changed: 52 additions & 3 deletions

File tree

extensions/mattermost/src/mattermost/monitor.inbound-system-event.test.ts

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ function createRuntimeCore(
159159
shouldHandleTextCommands?: () => boolean;
160160
textHasControlCommand?: (text?: string) => boolean;
161161
createInboundDebouncer?: typeof createInboundDebouncer;
162+
verboseDebug?: (message: string) => void;
162163
} = {},
163164
) {
164165
const dispatchPreparedForTest = vi.fn(
@@ -223,9 +224,9 @@ function createRuntimeCore(
223224
current: () => cfg,
224225
},
225226
logging: {
226-
shouldLogVerbose: () => false,
227+
shouldLogVerbose: () => Boolean(overrides.verboseDebug),
227228
getChildLogger: () => ({
228-
debug: vi.fn(),
229+
debug: overrides.verboseDebug ?? vi.fn(),
229230
info: vi.fn(),
230231
warn: vi.fn(),
231232
error: vi.fn(),
@@ -447,6 +448,53 @@ describe("mattermost inbound user posts", () => {
447448
expect(ctx?.Provider).toBe("mattermost");
448449
});
449450

451+
it("keeps verbose inbound previews on complete UTF-16 boundaries", async () => {
452+
const socket = new FakeWebSocket();
453+
const abortController = new AbortController();
454+
const verboseDebug = vi.fn();
455+
mockState.abortController = abortController;
456+
mockState.runtimeCore = createRuntimeCore(testConfig, undefined, { verboseDebug });
457+
458+
const monitor = monitorMattermostProvider({
459+
config: testConfig,
460+
runtime: testRuntime(),
461+
abortSignal: abortController.signal,
462+
webSocketFactory: () => socket,
463+
});
464+
465+
await vi.waitFor(() => {
466+
expect(socket.openListenerCount).toBeGreaterThan(0);
467+
});
468+
socket.emitOpen();
469+
470+
await socket.emitMessage({
471+
event: "posted",
472+
data: {
473+
channel_id: "chan-1",
474+
channel_name: "town-square",
475+
channel_display_name: "Town Square",
476+
sender_name: "alice",
477+
post: JSON.stringify({
478+
id: "post-verbose-preview",
479+
channel_id: "chan-1",
480+
user_id: "user-1",
481+
message: `${"a".repeat(199)}😀tail`,
482+
create_at: 1_714_000_000_000,
483+
}),
484+
},
485+
broadcast: {
486+
channel_id: "chan-1",
487+
user_id: "user-1",
488+
},
489+
});
490+
socket.emitClose(1000);
491+
await monitor;
492+
493+
expect(verboseDebug).toHaveBeenCalledWith(
494+
`mattermost inbound: from=mattermost:channel:chan-1 len=205 preview="${"a".repeat(199)}"`,
495+
);
496+
});
497+
450498
it("dispatches a bare bot mention whose body is empty after normalization as a wake event", async () => {
451499
const socket = new FakeWebSocket();
452500
const abortController = new AbortController();

extensions/mattermost/src/mattermost/monitor.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
normalizeTrimmedStringList,
2525
uniqueStrings,
2626
} from "openclaw/plugin-sdk/string-coerce-runtime";
27+
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
2728
import { getMattermostRuntime } from "../runtime.js";
2829
import {
2930
resolveMattermostAccount,
@@ -1677,7 +1678,7 @@ export async function monitorMattermostProvider(opts: MonitorMattermostOpts = {}
16771678
agentId: route.agentId,
16781679
});
16791680

1680-
const previewLine = bodyText.slice(0, 200).replace(/\n/g, "\\n");
1681+
const previewLine = truncateUtf16Safe(bodyText, 200).replace(/\n/g, "\\n");
16811682
logVerboseMessage(
16821683
`mattermost inbound: from=${ctxPayload.From} len=${bodyText.length} preview="${previewLine}"`,
16831684
);

0 commit comments

Comments
 (0)