Skip to content

Commit 97cdf88

Browse files
maweibinclaude
andcommitted
fix(audit): keep emoji / surrogate pairs intact during audit text truncation
Use truncateUtf16Safe() instead of .slice() to count full code points, preventing lone surrogates in audit CLI output. Co-Authored-By: Claude Opus 4.8 <[email protected]>
1 parent 775ef96 commit 97cdf88

2 files changed

Lines changed: 17 additions & 1 deletion

File tree

src/commands/audit.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,16 @@ describe("audit command parsing", () => {
4141
expect(row).toContain("run\\tcolumn");
4242
});
4343
});
44+
45+
describe("audit text truncation", () => {
46+
it("does not split surrogate pairs", async () => {
47+
const { truncateUtf16Safe } = await import("@openclaw/normalization-core/utf16-slice");
48+
const content = "x".repeat(78) + "🚀tail";
49+
// .slice(0, 79) would cut in the middle of the surrogate pair
50+
const bad = content.slice(0, 79);
51+
expect(bad.endsWith("\uD83D")).toBe(true);
52+
// truncateUtf16Safe drops the broken surrogate, keeping only 78 x's
53+
const good = truncateUtf16Safe(content, 79);
54+
expect(good).toBe("x".repeat(78));
55+
});
56+
});

src/commands/audit.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/** Operator CLI for bounded metadata-only run/tool audit pages. */
22
import { timestampMsToIsoString } from "@openclaw/normalization-core/number-coercion";
3+
import { truncateUtf16Safe } from "@openclaw/normalization-core/utf16-slice";
34
import type {
45
AuditEvent,
56
AuditListParams,
@@ -63,7 +64,9 @@ function short(value: string | undefined, maxChars: number): string {
6364
if (!sanitized) {
6465
return "-";
6566
}
66-
return sanitized.length <= maxChars ? sanitized : `${sanitized.slice(0, maxChars - 1)}…`;
67+
return sanitized.length <= maxChars
68+
? sanitized
69+
: `${truncateUtf16Safe(sanitized, maxChars - 1)}…`;
6770
}
6871

6972
function formatAuditRows(events: AuditEvent[]): string[] {

0 commit comments

Comments
 (0)