From c0265460630a493fc4180ab2faf94334b05a0b85 Mon Sep 17 00:00:00 2001 From: Masato Hoshino Date: Mon, 29 Jun 2026 04:14:45 +0900 Subject: [PATCH] fix(signal): sanitize internal tool-trace lines from outbound text (#97360) Wrap the signal outbound sanitizeText hook with sanitizeAssistantVisibleText so assistant internal tool-trace scaffolding is stripped before delivery, matching the sibling channel fixes under #90684 (Telegram #95774, Google Chat #95084, IRC #97214). --- extensions/signal/src/channel.ts | 2 ++ .../src/outbound-tool-trace-sanitize.test.ts | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 extensions/signal/src/outbound-tool-trace-sanitize.test.ts diff --git a/extensions/signal/src/channel.ts b/extensions/signal/src/channel.ts index a271935395f..77252ffc6ad 100644 --- a/extensions/signal/src/channel.ts +++ b/extensions/signal/src/channel.ts @@ -22,6 +22,7 @@ import { createDefaultChannelRuntimeState, } from "openclaw/plugin-sdk/status-helpers"; import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime"; +import { sanitizeAssistantVisibleText } from "openclaw/plugin-sdk/text-chunking"; import { resolveSignalAccount, type ResolvedSignalAccount } from "./accounts.js"; import { shouldSuppressLocalSignalExecApprovalPrompt, @@ -461,6 +462,7 @@ export const signalPlugin: ChannelPlugin = chunker: chunkText, chunkerMode: "text", textChunkLimit: 4000, + sanitizeText: ({ text }) => sanitizeAssistantVisibleText(text), shouldSuppressLocalPayloadPrompt: ({ cfg, accountId, payload, hint }) => shouldSuppressLocalSignalExecApprovalPrompt({ cfg, diff --git a/extensions/signal/src/outbound-tool-trace-sanitize.test.ts b/extensions/signal/src/outbound-tool-trace-sanitize.test.ts new file mode 100644 index 00000000000..54e0bcfbcc3 --- /dev/null +++ b/extensions/signal/src/outbound-tool-trace-sanitize.test.ts @@ -0,0 +1,19 @@ +// Signal outbound must strip assistant internal tool-trace scaffolding, matching +// the sibling channel fixes tracked under #90684 (Telegram #95774 / Google Chat +// #95084 / IRC #97214). Signal is plaintext-only, so leaked traces are verbatim. +import { describe, expect, it } from "vitest"; +import { signalPlugin } from "./channel.js"; + +describe("signal outbound sanitizeText", () => { + it("strips internal tool-trace banners before outbound delivery", () => { + const text = "Done.\n⚠️ 🛠️ `search repos (agent)` failed"; + + expect(signalPlugin.outbound?.sanitizeText?.({ text, payload: { text } })).toBe("Done."); + }); + + it("preserves ordinary assistant prose while sanitizing", () => { + const text = "The pipeline has 3 open deals."; + + expect(signalPlugin.outbound?.sanitizeText?.({ text, payload: { text } })).toBe(text); + }); +});