diff --git a/src/agents/pi-embedded-utils.test.ts b/src/agents/pi-embedded-utils.test.ts index c765a4d3a..5e90079a6 100644 --- a/src/agents/pi-embedded-utils.test.ts +++ b/src/agents/pi-embedded-utils.test.ts @@ -1,6 +1,6 @@ import type { AssistantMessage } from "@mariozechner/pi-ai"; import { describe, expect, it } from "vitest"; -import { extractAssistantText } from "./pi-embedded-utils.js"; +import { extractAssistantText, formatReasoningMessage } from "./pi-embedded-utils.js"; describe("extractAssistantText", () => { it("strips Minimax tool invocation XML from text", () => { @@ -508,3 +508,28 @@ File contents here`, expect(result).toBe("StartMiddleEnd"); }); }); + +describe("formatReasoningMessage", () => { + it("returns empty string for empty input", () => { + expect(formatReasoningMessage("")).toBe(""); + expect(formatReasoningMessage(" ")).toBe(""); + }); + + it("wraps single line in italics", () => { + expect(formatReasoningMessage("Because it helps")).toBe("Reasoning:\n_Because it helps_"); + }); + + it("wraps each line individually for multiline text", () => { + const input = "line one\nline two\nline three"; + expect(formatReasoningMessage(input)).toBe("Reasoning:\n_line one_\n_line two_\n_line three_"); + }); + + it("preserves empty lines without wrapping them", () => { + const input = "line one\n\nline three"; + expect(formatReasoningMessage(input)).toBe("Reasoning:\n_line one_\n\n_line three_"); + }); + + it("trims leading and trailing whitespace from input", () => { + expect(formatReasoningMessage(" text ")).toBe("Reasoning:\n_text_"); + }); +}); diff --git a/src/agents/pi-embedded-utils.ts b/src/agents/pi-embedded-utils.ts index 89a9df805..69be08971 100644 --- a/src/agents/pi-embedded-utils.ts +++ b/src/agents/pi-embedded-utils.ts @@ -209,9 +209,15 @@ export function extractAssistantThinking(msg: AssistantMessage): string { export function formatReasoningMessage(text: string): string { const trimmed = text.trim(); if (!trimmed) return ""; - // Show reasoning in italics (cursive) for markdown-friendly surfaces (Discord, etc.). + // Show reasoning in italics (cursive) for markdown-friendly surfaces. + // Wrap each line individually because many markdown parsers (Telegram, etc.) + // don't support underscores spanning multiple lines. // Keep the plain "Reasoning:" prefix so existing parsing/detection keeps working. - return `Reasoning:\n_${trimmed}_`; + const italicLines = trimmed + .split("\n") + .map((line) => (line ? `_${line}_` : "")) + .join("\n"); + return `Reasoning:\n${italicLines}`; } type ThinkTaggedSplitBlock =