mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-11 05:56:08 +00:00
fix(tasks): keep emoji / surrogate pairs intact during terminal output truncation (#101600)
* fix(tasks): keep emoji / surrogate pairs intact during terminal output truncation .slice() counts UTF-16 code units; use Array.from() to count full code points instead, preventing lone surrogates in task list output. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(tasks): preserve terminal column width --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
@@ -24,6 +24,7 @@ import type { OpenClawTestState } from "../test-utils/openclaw-test-state.js";
|
||||
import {
|
||||
tasksAuditCommand,
|
||||
tasksCancelCommand,
|
||||
tasksListCommand,
|
||||
tasksMaintenanceCommand,
|
||||
tasksShowCommand,
|
||||
} from "./tasks.js";
|
||||
@@ -601,6 +602,30 @@ describe("tasks commands", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps task list summaries within their UTF-16 column limit", async () => {
|
||||
await withTaskCommandStateDir(async () => {
|
||||
createTaskRecord({
|
||||
runtime: "cli",
|
||||
ownerKey: "agent:main:main",
|
||||
scopeKind: "session",
|
||||
runId: "task-utf16-summary",
|
||||
status: "succeeded",
|
||||
task: "Inspect task summary",
|
||||
terminalSummary: `${"y".repeat(78)}🚀xx`,
|
||||
});
|
||||
const runtime = createRuntime();
|
||||
|
||||
await tasksListCommand({}, runtime);
|
||||
|
||||
const output = vi
|
||||
.mocked(runtime.log)
|
||||
.mock.calls.map(([line]) => String(line))
|
||||
.join("\n");
|
||||
expect(output).toContain(`${"y".repeat(78)}…`);
|
||||
expect(output).not.toContain("🚀");
|
||||
});
|
||||
});
|
||||
|
||||
it("explains retained lost task cleanup timing in maintenance text output", async () => {
|
||||
await withTaskCommandStateDir(async () => {
|
||||
const cleanupAfter = Date.now() + 60_000;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
import { timestampMsToIsoString } from "@openclaw/normalization-core/number-coercion";
|
||||
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
|
||||
import { truncateUtf16Safe } from "@openclaw/normalization-core/utf16-slice";
|
||||
import { isRich, theme } from "../../packages/terminal-core/src/theme.js";
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import { formatLookupMiss } from "../cli/error-format.js";
|
||||
@@ -205,10 +206,7 @@ function truncate(value: string, maxChars: number) {
|
||||
if (value.length <= maxChars) {
|
||||
return value;
|
||||
}
|
||||
if (maxChars <= 1) {
|
||||
return value.slice(0, maxChars);
|
||||
}
|
||||
return `${value.slice(0, maxChars - 1)}…`;
|
||||
return maxChars <= 0 ? "" : `${truncateUtf16Safe(value, maxChars - 1)}…`;
|
||||
}
|
||||
|
||||
function shortToken(value: string | undefined, maxChars = ID_PAD): string {
|
||||
|
||||
Reference in New Issue
Block a user