Skip to content

Commit a515316

Browse files
authored
test(usage): cover UTF-16-safe log truncation
1 parent 9319cfb commit a515316

2 files changed

Lines changed: 22 additions & 2 deletions

File tree

src/infra/session-cost-usage.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2919,6 +2919,25 @@ example
29192919
expect(logs?.[0]?.content).toBe("hello there");
29202920
});
29212921

2922+
it("does not split surrogate pairs when truncating session log content", async () => {
2923+
const root = await makeSessionCostRoot("logs-utf16");
2924+
const sessionFile = path.join(root, "session.jsonl");
2925+
const content = "x".repeat(1999) + "🚀tail";
2926+
await fs.writeFile(
2927+
sessionFile,
2928+
JSON.stringify({
2929+
type: "message",
2930+
timestamp: "2026-02-21T17:47:00.000Z",
2931+
message: { role: "assistant", content },
2932+
}),
2933+
"utf-8",
2934+
);
2935+
2936+
const logs = await loadSessionLogs({ sessionFile });
2937+
2938+
expect(logs?.[0]?.content).toBe(`${"x".repeat(1999)}…`);
2939+
});
2940+
29222941
it("normalizes malformed log timestamps with the transcript timestamp rules", async () => {
29232942
const root = await makeSessionCostRoot("logs-malformed-timestamp");
29242943
const sessionsDir = path.join(root, "agents", "main", "sessions");

src/infra/session-cost-usage.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import path from "node:path";
44
import readline from "node:readline";
55
import { asFiniteNumber } from "@openclaw/normalization-core/number-coercion";
66
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
7+
import { truncateUtf16Safe } from "@openclaw/normalization-core/utf16-slice";
78
import type { NormalizedUsage, UsageLike } from "../agents/usage.js";
89
import { normalizeUsage } from "../agents/usage.js";
910
import { stripInboundMetadata } from "../auto-reply/reply/strip-inbound-meta.js";
@@ -2733,10 +2734,10 @@ export async function loadSessionLogs(params: {
27332734
continue;
27342735
}
27352736

2736-
// Truncate very long content, keeping emoji / surrogate pairs intact.
2737+
// Truncate very long content.
27372738
const maxLen = 2000;
27382739
if (content.length > maxLen) {
2739-
content = [...content].slice(0, maxLen).join("") + "…";
2740+
content = truncateUtf16Safe(content, maxLen) + "…";
27402741
}
27412742

27422743
// Get timestamp

0 commit comments

Comments
 (0)