diff --git a/extensions/telegram/src/bot-message-dispatch.reasoning-room-events.test.ts b/extensions/telegram/src/bot-message-dispatch.reasoning-room-events.test.ts
index 0e4273a074ae..27463e28711f 100644
--- a/extensions/telegram/src/bot-message-dispatch.reasoning-room-events.test.ts
+++ b/extensions/telegram/src/bot-message-dispatch.reasoning-room-events.test.ts
@@ -188,21 +188,34 @@ describeTelegramDispatch("dispatchTelegramMessage reasoning-room-events", () =>
expect(deliverReplies).not.toHaveBeenCalled();
});
- it("suppresses internal reflection when reasoning streams", async () => {
- const { reasoningDraftStream } = setupDraftStreams({
+ it("suppresses whitespace-form internal prefixes until one visible final", async () => {
+ const { answerDraftStream, reasoningDraftStream } = setupDraftStreams({
answerMessageId: 2001,
reasoningMessageId: 3001,
});
mockTurn(async ({ dispatcherOptions }) => {
- await dispatcherOptions.deliver(
- { text: "private reflection", isReasoning: true },
- { kind: "final" },
- );
+ for (const text of [
+ "< internal",
+ "< internal",
+ " internal",
+ "< / internal",
+ "<\u00a0internal",
+ ]) {
+ await dispatcherOptions.deliver({ text, isReasoning: true }, { kind: "block" });
+ }
+ expect(reasoningDraftStream.update).not.toHaveBeenCalled();
+ expect(deliverReplies).not.toHaveBeenCalled();
+ await dispatcherOptions.deliver({ text: "VISIBLE" }, { kind: "final" });
});
await dispatchWithContext({ context: createReasoningStreamContext() });
expect(reasoningDraftStream.update).not.toHaveBeenCalled();
+ expect(answerDraftStream.update).toHaveBeenCalledTimes(1);
+ expect(answerDraftStream.update).toHaveBeenCalledWith(
+ "VISIBLE",
+ expect.objectContaining({ onPlatformSendDispatch: expect.any(Function) }),
+ );
expect(deliverReplies).not.toHaveBeenCalled();
});
diff --git a/extensions/telegram/src/reasoning-lane-coordinator.test.ts b/extensions/telegram/src/reasoning-lane-coordinator.test.ts
index f60aaaf0aec3..8523616fcf96 100644
--- a/extensions/telegram/src/reasoning-lane-coordinator.test.ts
+++ b/extensions/telegram/src/reasoning-lane-coordinator.test.ts
@@ -41,9 +41,22 @@ describe("splitTelegramReasoningText", () => {
});
});
- it("does not emit partial reasoning tag prefixes", () => {
- expect(splitTelegramReasoningText(" {
+ expect(splitTelegramReasoningText(text, true)).toStrictEqual({});
+ });
+
+ it("keeps unrelated partial tags visible", () => {
+ expect(splitTelegramReasoningText("< interface", true)).toStrictEqual({
+ reasoningText: "🧠_< interface_",
+ });
});
it("keeps visible Thinking-prefixed answers in the answer lane", () => {
diff --git a/extensions/telegram/src/reasoning-lane-coordinator.ts b/extensions/telegram/src/reasoning-lane-coordinator.ts
index 2ecc25f8e4e0..e2e1e70aa342 100644
--- a/extensions/telegram/src/reasoning-lane-coordinator.ts
+++ b/extensions/telegram/src/reasoning-lane-coordinator.ts
@@ -1,7 +1,6 @@
// Telegram plugin module implements reasoning lane coordinator behavior.
import { formatReasoningMessage } from "openclaw/plugin-sdk/agent-runtime";
import type { ReplyPayload } from "openclaw/plugin-sdk/reply-payload";
-import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
import {
findCodeRegions,
isInsideCode,
@@ -71,14 +70,14 @@ function extractThinkingFromTaggedStreamOutsideCode(text: string): string {
}
function isPartialReasoningTagPrefix(text: string): boolean {
- const trimmed = normalizeLowercaseStringOrEmpty(text.trimStart());
+ const trimmed = text.trim().replace(/^<\s*(\/?)\s+/u, "<$1");
if (!trimmed.startsWith("<")) {
return false;
}
if (trimmed.includes(">")) {
return false;
}
- return REASONING_TAG_PREFIXES.some((prefix) => prefix.startsWith(trimmed));
+ return REASONING_TAG_PREFIXES.some((prefix) => prefix.startsWith(trimmed.toLowerCase()));
}
type TelegramReasoningSplit = {