diff --git a/CHANGELOG.md b/CHANGELOG.md index c02fb1d6c0e..d706a186f4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,7 +56,7 @@ Docs: https://docs.openclaw.ai - Security/Audit: warn when `gateway.tools.allow` re-enables default-denied tools over HTTP `POST /tools/invoke`, since this can increase RCE blast radius if the gateway is reachable. - Security/Plugins/Hooks: harden npm-based installs by restricting specs to registry packages only, passing `--ignore-scripts` to `npm pack`, and cleaning up temp install directories. - Feishu: stop persistent Typing reaction on NO_REPLY/suppressed runs by wiring reply-dispatcher cleanup to remove typing indicators. (#15464) Thanks @arosstale. -- Agents: strip leading whitespace/newlines from `sanitizeUserFacingText` output and normalize whitespace-only outputs to empty text. (#16158) Thanks @mcinteerj. +- Agents: strip leading empty lines from `sanitizeUserFacingText` output and normalize whitespace-only outputs to empty text. (#16158) Thanks @mcinteerj. - BlueBubbles: gracefully degrade when Private API is disabled by filtering private-only actions, skipping private-only reactions/reply effects, and avoiding private reply markers so non-private flows remain usable. (#16002) Thanks @L-U-C-K-Y. - Outbound: add a write-ahead delivery queue with crash-recovery retries to prevent lost outbound messages after gateway restarts. (#15636) Thanks @nabbilkhan, @thewilloftheshadow. - Auto-reply/Threading: auto-inject implicit reply threading so `replyToMode` works without requiring model-emitted `[[reply_to_current]]`, while preserving `replyToMode: "off"` behavior for implicit Slack replies and keeping block-streaming chunk coalescing stable under `replyToMode: "first"`. (#14976) Thanks @Diaspar4u. diff --git a/src/agents/pi-embedded-helpers.sanitizeuserfacingtext.e2e.test.ts b/src/agents/pi-embedded-helpers.sanitizeuserfacingtext.e2e.test.ts index cd8b13c3ee5..e666556ee1f 100644 --- a/src/agents/pi-embedded-helpers.sanitizeuserfacingtext.e2e.test.ts +++ b/src/agents/pi-embedded-helpers.sanitizeuserfacingtext.e2e.test.ts @@ -77,7 +77,7 @@ describe("sanitizeUserFacingText", () => { }); it("strips leading whitespace and newlines combined", () => { - expect(sanitizeUserFacingText("\n \n Hello")).toBe("Hello"); + expect(sanitizeUserFacingText("\n \nHello")).toBe("Hello"); expect(sanitizeUserFacingText(" \n\nHello")).toBe("Hello"); }); diff --git a/src/agents/pi-embedded-helpers/errors.ts b/src/agents/pi-embedded-helpers/errors.ts index 02332145ad7..1d3c9846316 100644 --- a/src/agents/pi-embedded-helpers/errors.ts +++ b/src/agents/pi-embedded-helpers/errors.ts @@ -527,7 +527,10 @@ export function sanitizeUserFacingText(text: string, opts?: { errorContext?: boo } } - return collapseConsecutiveDuplicateBlocks(stripped).trimStart(); + // Strip leading blank lines (including whitespace-only lines) without clobbering indentation on + // the first content line (e.g. markdown/code blocks). + const withoutLeadingEmptyLines = stripped.replace(/^(?:[ \t]*\r?\n)+/, ""); + return collapseConsecutiveDuplicateBlocks(withoutLeadingEmptyLines); } export function isRateLimitAssistantError(msg: AssistantMessage | undefined): boolean {