From 1a3ce7c2a8da266cd0bb2844d44ba5af3942908b Mon Sep 17 00:00:00 2001 From: Chunyue Wang <80630709+openperf@users.noreply.github.com> Date: Fri, 5 Jun 2026 14:57:16 +0800 Subject: [PATCH] fix(qqbot): sanitize outbound text to strip reasoning/thinking content (#90132) Summary: - Adds QQBot outbound `sanitizeText` wired to `sanitizeAssistantVisibleText` plus a regression test for stripping `` and `` blocks. - PR surface: Source +2, Tests +19. Total +21 across 2 files. - Reproducibility: yes. source-reproducible: current main QQBot outbound lacks `sanitizeText`, and shared deli ... nnel text sanitization when that hook exists. I did not run a live Tencent QQBot plus MiniMax reproduction. Automerge notes: - PR branch already contained follow-up commit before automerge: fix(qqbot): add curly braces for eslint(curly) compliance Validation: - ClawSweeper review passed for head 17cf140183e84fbca47325746dea3afa2005b6d3. - Required merge gates passed before the squash merge. Prepared head SHA: 17cf140183e84fbca47325746dea3afa2005b6d3 Review: https://github.com/openclaw/openclaw/pull/90132#issuecomment-4618527026 Co-authored-by: openperf <16864032@qq.com> Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com> Approved-by: takhoffman Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com> --- .../qqbot/src/channel.message-adapter.test.ts | 19 +++++++++++++++++++ extensions/qqbot/src/channel.ts | 2 ++ 2 files changed, 21 insertions(+) diff --git a/extensions/qqbot/src/channel.message-adapter.test.ts b/extensions/qqbot/src/channel.message-adapter.test.ts index 27509445e579..f49f44dc88c2 100644 --- a/extensions/qqbot/src/channel.message-adapter.test.ts +++ b/extensions/qqbot/src/channel.message-adapter.test.ts @@ -4,6 +4,25 @@ import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts"; import { describe, expect, it, vi } from "vitest"; import { qqbotPlugin } from "./channel.js"; +describe("qqbot outbound sanitizeText", () => { + it("strips reasoning/thinking tags before delivery", () => { + const sanitize = qqbotPlugin.outbound?.sanitizeText; + expect(sanitize).toBeDefined(); + if (!sanitize) { + return; + } + + const input1 = "internal reasoningfinal answer"; + expect(sanitize({ text: input1, payload: { text: input1 } })).toBe("final answer"); + + const input2 = "step by stepresult"; + expect(sanitize({ text: input2, payload: { text: input2 } })).toBe("result"); + + const input3 = "plain text without tags"; + expect(sanitize({ text: input3, payload: { text: input3 } })).toBe("plain text without tags"); + }); +}); + const sendTextMock = vi.hoisted(() => vi.fn()); const sendMediaMock = vi.hoisted(() => vi.fn()); diff --git a/extensions/qqbot/src/channel.ts b/extensions/qqbot/src/channel.ts index 3df1652f6bae..b9442cbeb3de 100644 --- a/extensions/qqbot/src/channel.ts +++ b/extensions/qqbot/src/channel.ts @@ -8,6 +8,7 @@ import { } from "openclaw/plugin-sdk/channel-outbound"; import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts"; import type { ChannelPlugin } from "openclaw/plugin-sdk/core"; +import { sanitizeAssistantVisibleText } from "openclaw/plugin-sdk/text-chunking"; // Register the PlatformAdapter before any core/ module is used. import "./bridge/bootstrap.js"; import { getQQBotApprovalCapability } from "./bridge/approval/capability.js"; @@ -254,6 +255,7 @@ export const qqbotPlugin: ChannelPlugin = { chunker: (text, limit) => getQQBotRuntime().channel.text.chunkMarkdownText(text, limit), chunkerMode: "markdown", textChunkLimit: 5000, + sanitizeText: ({ text }) => sanitizeAssistantVisibleText(text), shouldSuppressLocalPayloadPrompt: ({ cfg, accountId, payload, hint }) => shouldSuppressLocalQQBotApprovalPrompt({ cfg,