mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 04:47:03 -06:00
fix(telegram): keep streamed tool-progress lines on separate lines
Telegram's rich-markdown renderer treats a lone "\n" as a soft break (rendered as a space), so streamed tool-progress draft lines joined by a single newline collapsed onto one line. Pass "\n\n" as the progress-draft line separator for Telegram; it renders a blank line as a single break, so each tool/thinking/commentary line gets its own line again. Other channels keep the single-newline default, so Discord and the rest are unaffected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
committed by
Ayaan Zaidi
parent
50c82b3020
commit
c847db550f
@@ -2374,7 +2374,7 @@ describe("dispatchTelegramMessage draft streaming", () => {
|
||||
});
|
||||
|
||||
expect(answerDraftStream.update).toHaveBeenCalledWith(
|
||||
"Cracking\n\n`🛠️ Exec`\n`🛠️ git rev-parse --abbrev-ref HEAD`",
|
||||
"Cracking\n\n`🛠️ Exec`\n\n`🛠️ git rev-parse --abbrev-ref HEAD`",
|
||||
);
|
||||
expect(answerDraftStream.update).not.toHaveBeenCalledWith("Branch is up to date");
|
||||
expect(answerDraftStream.forceNewMessage).toHaveBeenCalledTimes(1);
|
||||
@@ -2434,7 +2434,7 @@ describe("dispatchTelegramMessage draft streaming", () => {
|
||||
expect.stringContaining("stdout line one"),
|
||||
);
|
||||
expect(answerDraftStream.update).toHaveBeenLastCalledWith(
|
||||
"Shelling\n\n`🛠️ Exec`\n`🔎 Web Search: docs lookup`",
|
||||
"Shelling\n\n`🛠️ Exec`\n\n`🔎 Web Search: docs lookup`",
|
||||
);
|
||||
expect(deliverReplies).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -2670,7 +2670,7 @@ describe("dispatchTelegramMessage draft streaming", () => {
|
||||
});
|
||||
|
||||
expect(createTelegramDraftStream).toHaveBeenCalledTimes(1);
|
||||
expect(draftStream.update).toHaveBeenCalledWith("Shelling\n\n`🛠️ Exec`\n• _Checking files_");
|
||||
expect(draftStream.update).toHaveBeenCalledWith("Shelling\n\n`🛠️ Exec`\n\n• _Checking files_");
|
||||
});
|
||||
|
||||
it("renders configured Telegram commentary progress from preamble item events", async () => {
|
||||
@@ -2840,7 +2840,7 @@ describe("dispatchTelegramMessage draft streaming", () => {
|
||||
});
|
||||
|
||||
expect(draftStream.update).toHaveBeenCalledWith(
|
||||
"Shelling\n\n`🔎 Web Search: docs lookup`\n• `tests passed`",
|
||||
"Shelling\n\n`🔎 Web Search: docs lookup`\n\n• `tests passed`",
|
||||
);
|
||||
expect(draftStream.forceNewMessage).toHaveBeenCalledTimes(1);
|
||||
expect(draftStream.materialize).not.toHaveBeenCalled();
|
||||
|
||||
@@ -967,6 +967,10 @@ export const dispatchTelegramMessage = async ({
|
||||
active: Boolean(answerLane.stream),
|
||||
seed: progressSeed,
|
||||
formatLine: formatTelegramProgressLine,
|
||||
// Telegram's rich-markdown renderer collapses a lone "\n" to a space, so
|
||||
// tool-progress lines need a blank line between them to stay on their own
|
||||
// line (it renders "\n\n" as a single break, not a double).
|
||||
lineSeparator: "\n\n",
|
||||
update: async (streamText, options) => {
|
||||
await prepareAnswerLaneForToolProgress();
|
||||
answerLane.lastPartialText = streamText;
|
||||
|
||||
@@ -39,6 +39,9 @@ export function createChannelProgressDraftCompositor(params: {
|
||||
deleteCurrent?: () => Promise<void> | void;
|
||||
tryNativeUpdate?: (text: string) => Promise<boolean> | boolean;
|
||||
formatLine?: (line: string) => string;
|
||||
/** Separator between rendered draft lines; forwarded to the draft formatter.
|
||||
* Telegram passes "\n\n" because its renderer collapses a lone newline. */
|
||||
lineSeparator?: string;
|
||||
isEmptyLine?: (line: ProgressDraftLine | undefined) => boolean;
|
||||
shouldStartNow?: (line: ProgressDraftLine | undefined) => boolean;
|
||||
}) {
|
||||
@@ -66,6 +69,7 @@ export function createChannelProgressDraftCompositor(params: {
|
||||
lines: draftLines,
|
||||
seed: params.seed,
|
||||
formatLine: options?.formatted === false ? undefined : params.formatLine,
|
||||
lineSeparator: params.lineSeparator,
|
||||
});
|
||||
|
||||
const clearProgressState = (suppressed: boolean) => {
|
||||
|
||||
@@ -1047,6 +1047,11 @@ export function formatChannelProgressDraftText(params: {
|
||||
formatLine?: (line: string) => string;
|
||||
/** Prefix used for plain progress lines that lack their own icon. */
|
||||
bullet?: string;
|
||||
/** Separator between rendered progress lines. Defaults to a single newline.
|
||||
* Telegram's rich-markdown parser treats a lone "\n" as a soft break (space),
|
||||
* so its draft passes "\n\n"; channels where a single newline already breaks
|
||||
* the line (Discord) keep the default. */
|
||||
lineSeparator?: string;
|
||||
}): string {
|
||||
const rawLabel = resolveChannelProgressDraftLabel({
|
||||
entry: params.entry,
|
||||
@@ -1058,6 +1063,7 @@ export function formatChannelProgressDraftText(params: {
|
||||
const maxLineChars = resolveChannelProgressDraftMaxLineChars(params.entry);
|
||||
const formatLine = params.formatLine ?? ((line: string) => line);
|
||||
const bullet = params.bullet ?? "•";
|
||||
const lineSeparator = params.lineSeparator ?? "\n";
|
||||
const rawLines: Array<string | ChannelProgressDraftLine | { draftLabel: string }> = resolvedLabel
|
||||
? [{ draftLabel: resolvedLabel }, ...params.lines]
|
||||
: params.lines;
|
||||
@@ -1090,7 +1096,7 @@ export function formatChannelProgressDraftText(params: {
|
||||
});
|
||||
const renderedLines = lines.map((line) => line.text).filter((line) => Boolean(line));
|
||||
if (renderedLines.length > 1 && lines[0]?.isLabelLine) {
|
||||
return `${renderedLines[0]}\n\n${renderedLines.slice(1).join("\n")}`;
|
||||
return `${renderedLines[0]}\n\n${renderedLines.slice(1).join(lineSeparator)}`;
|
||||
}
|
||||
return renderedLines.join("\n");
|
||||
return renderedLines.join(lineSeparator);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user