From d90a94ad165b9e49fda18f2f92aefdf950ba8b03 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sat, 6 Jun 2026 08:23:55 -0700 Subject: [PATCH] fix(channels): strip dangling progress italics --- src/channels/streaming.ts | 21 +++++++++++++++++++-- src/plugin-sdk/channel-streaming.test.ts | 16 ++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/channels/streaming.ts b/src/channels/streaming.ts index 073f5e26bbf5..ed9c5aa76f9d 100644 --- a/src/channels/streaming.ts +++ b/src/channels/streaming.ts @@ -901,6 +901,23 @@ function removeUnbalancedInlineBackticks(value: string): string { return value.trimStart().startsWith("`") ? value.replaceAll("`", "'") : value.replaceAll("`", ""); } +function repairCompactedProgressMarkdown(value: string): string { + const withoutDanglingBackticks = removeUnbalancedInlineBackticks(value); + const trimmedStart = withoutDanglingBackticks.trimStart(); + if (!trimmedStart.startsWith("_") || trimmedStart.endsWith("_")) { + return withoutDanglingBackticks; + } + const underscoreCount = Array.from(trimmedStart).filter((char) => char === "_").length; + if (underscoreCount % 2 === 0) { + return withoutDanglingBackticks; + } + const leadingWhitespace = withoutDanglingBackticks.slice( + 0, + withoutDanglingBackticks.length - trimmedStart.length, + ); + return `${leadingWhitespace}${trimmedStart.slice(1)}`; +} + function compactPlainProgressLine(line: string, maxChars: number): string { const head = sliceCodePoints(line, 0, maxChars - 1).trimEnd(); const boundary = head.search(/\s+\S*$/u); @@ -931,7 +948,7 @@ function compactChannelProgressDraftLine(line: string, maxChars: number): string } // Keep the stable tool label/icon visible while trimming volatile command // detail; this reduces progress draft edit churn in chat UIs. - return removeUnbalancedInlineBackticks( + return repairCompactedProgressMarkdown( `${prefix}${compactProgressLineDetail(detail, detailLimit)}`, ); }; @@ -954,7 +971,7 @@ function compactChannelProgressDraftLine(line: string, maxChars: number): string } } - return removeUnbalancedInlineBackticks(compactPlainProgressLine(normalized, maxChars)); + return repairCompactedProgressMarkdown(compactPlainProgressLine(normalized, maxChars)); } function getProgressDraftLineText(line: string | ChannelProgressDraftLine): string { diff --git a/src/plugin-sdk/channel-streaming.test.ts b/src/plugin-sdk/channel-streaming.test.ts index b12bc1973455..19d23b725dc4 100644 --- a/src/plugin-sdk/channel-streaming.test.ts +++ b/src/plugin-sdk/channel-streaming.test.ts @@ -361,6 +361,22 @@ describe("channel-streaming", () => { ).toBe("Shelling\n\n• I'm checking whether the generated video exists or if the…"); }); + it("falls back to plain commentary when compaction drops the closing italic marker", () => { + expect( + formatChannelProgressDraftText({ + entry: { streaming: { progress: { label: false, maxLineChars: 32 } } }, + lines: [ + { + kind: "item", + text: `_${"x".repeat(80)}_`, + label: "Commentary", + prefix: false, + }, + ], + }), + ).toBe(`${"x".repeat(30)}…`); + }); + it("keeps compacted raw progress lines from leaking unmatched markdown backticks", () => { const line = buildChannelProgressDraftLine( {