fix(coding-agent): preserve backslash escapes in user messages

closes #6105
This commit is contained in:
Armin Ronacher
2026-06-27 19:23:52 +02:00
parent 622eca7608
commit f2e9d75388
5 changed files with 32 additions and 1 deletions
+1
View File
@@ -5,6 +5,7 @@
### Fixed
- Fixed `--session` and `SessionManager.open()` to reject non-empty invalid session files without overwriting them ([#6002](https://github.com/earendil-works/pi/issues/6002)).
- Fixed user-message transcript rendering to keep visible backslashes in Markdown escape sequences such as `\"` ([#6105](https://github.com/earendil-works/pi/issues/6105)).
- Fixed assistant messages stopped by output length to show a visible incomplete-response error ([#4290](https://github.com/earendil-works/pi/issues/4290)).
- Fixed `--no-session --session-id` so ephemeral CLI runs can use deterministic session IDs for provider cache affinity ([#6070](https://github.com/earendil-works/pi/issues/6070)).
- Fixed disk BMP image files to be detected, converted to PNG, and attached through `read` and CLI `@file` inputs ([#6047](https://github.com/earendil-works/pi/issues/6047)).
@@ -23,7 +23,7 @@ export class UserMessageComponent extends Container {
{
color: (content: string) => theme.fg("userMessageText", content),
},
{ preserveOrderedListMarkers: true },
{ preserveOrderedListMarkers: true, preserveBackslashEscapes: true },
),
);
this.addChild(this.contentBox);
+4
View File
@@ -2,6 +2,10 @@
## [Unreleased]
### Added
- Added an opt-in Markdown renderer option to preserve source backslash escapes for transcript rendering ([#6105](https://github.com/earendil-works/pi/issues/6105)).
## [0.80.2] - 2026-06-23
## [0.80.1] - 2026-06-23
+6
View File
@@ -98,6 +98,8 @@ export interface MarkdownTheme {
export interface MarkdownOptions {
/** Preserve source list markers instead of normalizing them. */
preserveOrderedListMarkers?: boolean;
/** Preserve source backslash escapes instead of normalizing escaped punctuation. */
preserveBackslashEscapes?: boolean;
}
interface InlineStyleContext {
@@ -498,6 +500,10 @@ export class Markdown implements Component {
for (const token of tokens) {
switch (token.type) {
case "escape":
result += applyTextWithNewlines(this.options.preserveBackslashEscapes ? token.raw : token.text);
break;
case "text":
// Text tokens in list items can have nested tokens for inline formatting
if (token.tokens && token.tokens.length > 0) {
+20
View File
@@ -654,6 +654,26 @@ describe("Markdown component", () => {
});
});
describe("Backslash escapes", () => {
it("should normalize escaped punctuation by default", () => {
const markdown = new Markdown(String.raw`"\"`, 0, 0, defaultMarkdownTheme);
const lines = markdown.render(80).map((line) => stripAnsi(line).trimEnd());
assert.deepStrictEqual(lines, [`""`]);
});
it("should preserve source backslash escapes when configured", () => {
const markdown = new Markdown(String.raw`"\"`, 0, 0, defaultMarkdownTheme, undefined, {
preserveBackslashEscapes: true,
});
const lines = markdown.render(80).map((line) => stripAnsi(line).trimEnd());
assert.deepStrictEqual(lines, [String.raw`"\"`]);
});
});
describe("Pre-styled text (thinking traces)", () => {
it("should preserve gray italic styling after inline code", () => {
// This replicates how thinking content is rendered in assistant-message.ts