fix(coding-agent): preserve backslash escapes in user messages
closes #6105
This commit is contained in:
@@ -5,6 +5,7 @@
|
|||||||
### Fixed
|
### 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 `--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 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 `--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)).
|
- 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),
|
color: (content: string) => theme.fg("userMessageText", content),
|
||||||
},
|
},
|
||||||
{ preserveOrderedListMarkers: true },
|
{ preserveOrderedListMarkers: true, preserveBackslashEscapes: true },
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
this.addChild(this.contentBox);
|
this.addChild(this.contentBox);
|
||||||
|
|||||||
@@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
## [Unreleased]
|
## [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.2] - 2026-06-23
|
||||||
|
|
||||||
## [0.80.1] - 2026-06-23
|
## [0.80.1] - 2026-06-23
|
||||||
|
|||||||
@@ -98,6 +98,8 @@ export interface MarkdownTheme {
|
|||||||
export interface MarkdownOptions {
|
export interface MarkdownOptions {
|
||||||
/** Preserve source list markers instead of normalizing them. */
|
/** Preserve source list markers instead of normalizing them. */
|
||||||
preserveOrderedListMarkers?: boolean;
|
preserveOrderedListMarkers?: boolean;
|
||||||
|
/** Preserve source backslash escapes instead of normalizing escaped punctuation. */
|
||||||
|
preserveBackslashEscapes?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface InlineStyleContext {
|
interface InlineStyleContext {
|
||||||
@@ -498,6 +500,10 @@ export class Markdown implements Component {
|
|||||||
|
|
||||||
for (const token of tokens) {
|
for (const token of tokens) {
|
||||||
switch (token.type) {
|
switch (token.type) {
|
||||||
|
case "escape":
|
||||||
|
result += applyTextWithNewlines(this.options.preserveBackslashEscapes ? token.raw : token.text);
|
||||||
|
break;
|
||||||
|
|
||||||
case "text":
|
case "text":
|
||||||
// Text tokens in list items can have nested tokens for inline formatting
|
// Text tokens in list items can have nested tokens for inline formatting
|
||||||
if (token.tokens && token.tokens.length > 0) {
|
if (token.tokens && token.tokens.length > 0) {
|
||||||
|
|||||||
@@ -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)", () => {
|
describe("Pre-styled text (thinking traces)", () => {
|
||||||
it("should preserve gray italic styling after inline code", () => {
|
it("should preserve gray italic styling after inline code", () => {
|
||||||
// This replicates how thinking content is rendered in assistant-message.ts
|
// This replicates how thinking content is rendered in assistant-message.ts
|
||||||
|
|||||||
Reference in New Issue
Block a user