Skip to content

Commit 01243cc

Browse files
committed
fix(msteams): sanitize internal tool-trace lines from outbound text (#90684)
Rebased onto current main. Adds a sanitizeAssistantVisibleText() hook to the msteams outbound adapter so internal failed-tool trace banners are stripped before delivery (mirrors the merged Google Chat fix #95084). The adapter tests pass a ReplyPayload-compatible payload and use a single string literal so the exact-head typecheck and lint pass. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
1 parent 9826acd commit 01243cc

2 files changed

Lines changed: 25 additions & 2 deletions

File tree

extensions/msteams/src/channel.message-adapter.test.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ vi.mock("./channel.runtime.js", () => ({
2525
},
2626
}));
2727

28-
import { msteamsPlugin } from "./channel.js";
28+
import { msteamsChannelOutbound, msteamsPlugin } from "./channel.js";
2929

3030
type MSTeamsMessageAdapter = NonNullable<typeof msteamsPlugin.message>;
3131
type MSTeamsMessageSender = NonNullable<MSTeamsMessageAdapter["send"]>;
@@ -226,3 +226,21 @@ describe("msteams channel message adapter", () => {
226226
});
227227
});
228228
});
229+
230+
describe("msteams outbound sanitizeText", () => {
231+
const sanitizeText = msteamsChannelOutbound.sanitizeText;
232+
233+
it("strips internal tool-trace failure banners from outbound text (#90684)", () => {
234+
expect(sanitizeText).toBeDefined();
235+
const text = "Listo, ya lo cargué.\n⚠️ 🛠️ `grep -E 'PV-123' /tmp/out.txt (agent)` failed";
236+
const out = sanitizeText?.({ text, payload: { text } }) ?? text;
237+
expect(out).toBe("Listo, ya lo cargué.");
238+
expect(out).not.toContain("failed");
239+
expect(out).not.toContain("🛠️");
240+
});
241+
242+
it("preserves ordinary assistant prose untouched", () => {
243+
const text = "La factura PV-123 quedó imputada en Bejerman.";
244+
expect(sanitizeText?.({ text, payload: { text } }) ?? text).toBe(text);
245+
});
246+
});

extensions/msteams/src/channel.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {
2828
normalizeOptionalString,
2929
normalizeStringEntries,
3030
} from "openclaw/plugin-sdk/string-coerce-runtime";
31+
import { sanitizeAssistantVisibleText } from "openclaw/plugin-sdk/text-chunking";
3132
import { Type } from "typebox";
3233
import type {
3334
ChannelMessageActionName,
@@ -479,11 +480,15 @@ function describeMSTeamsMessageTool({
479480
};
480481
}
481482

482-
const msteamsChannelOutbound: ChannelOutboundAdapter = {
483+
export const msteamsChannelOutbound: ChannelOutboundAdapter = {
483484
deliveryMode: "direct",
484485
chunker: chunkTextForOutbound,
485486
chunkerMode: "markdown",
486487
textChunkLimit: 4000,
488+
// Strip internal assistant trace lines (e.g. `⚠️ 🛠️ ... (agent) failed`
489+
// banners from benign non-zero shell exits) before delivery, so they don't
490+
// leak to users — mirrors Discord/Google Chat/WhatsApp outbound. See #90684.
491+
sanitizeText: ({ text }: { text: string }) => sanitizeAssistantVisibleText(text),
487492
pollMaxOptions: 12,
488493
deliveryCapabilities: {
489494
durableFinal: {

0 commit comments

Comments
 (0)