Skip to content

Commit 49a5b6a

Browse files
lsr911claudesteipete
authored
fix(compaction): use truncateUtf16Safe for post-compaction context text (#102515)
* fix(compaction): use truncateUtf16Safe for post-compaction context text Replace naive .slice(0, maxContextChars) with truncateUtf16Safe() to prevent surrogate pair splitting in post-compaction context summaries sent to the model. Co-Authored-By: Claude <[email protected]> * test(compaction): prove context UTF-16 boundary --------- Co-authored-by: Claude <[email protected]> Co-authored-by: Peter Steinberger <[email protected]>
1 parent 8c608ae commit 49a5b6a

2 files changed

Lines changed: 18 additions & 1 deletion

File tree

src/auto-reply/reply/post-compaction-context.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,22 @@ Ignore this.
149149
expect(result?.length).toBeLessThan(2600);
150150
});
151151

152+
it("keeps truncated post-compaction context UTF-16 safe", async () => {
153+
const prefix = "A".repeat(159);
154+
fs.writeFileSync(path.join(tmpDir, "AGENTS.md"), `## Session Startup\n\n${prefix}😀tail`);
155+
const cfg = {
156+
agents: {
157+
defaults: {
158+
contextLimits: { postCompactionMaxChars: 180 },
159+
},
160+
},
161+
} as OpenClawConfig;
162+
163+
const result = await readDefaultPostCompactionContext({ cfg });
164+
165+
expect(result).toContain(`## Session Startup\n\n${prefix}\n...[truncated]...`);
166+
});
167+
152168
it("honors per-agent post-compaction context limit overrides", async () => {
153169
const longContent =
154170
"## Session Startup\n\n" + "B".repeat(4000) + "\n\n## Red Lines\n\nGuardrails.";

src/auto-reply/reply/post-compaction-context.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import fs from "node:fs";
33
import path from "node:path";
44
import { normalizeLowercaseStringOrEmpty } from "@openclaw/normalization-core/string-coerce";
5+
import { truncateUtf16Safe } from "@openclaw/normalization-core/utf16-slice";
56
import { resolveAgentContextLimits } from "../../agents/agent-scope.js";
67
import { resolveCronStyleNow } from "../../agents/current-time.js";
78
import { formatDateStamp, resolveUserTimezone } from "../../agents/date-time.js";
@@ -117,7 +118,7 @@ export async function readPostCompactionContext(
117118
const combined = sections.join("\n\n").replaceAll("YYYY-MM-DD", dateStamp);
118119
const safeContent =
119120
combined.length > maxContextChars
120-
? combined.slice(0, maxContextChars) + "\n...[truncated]..."
121+
? truncateUtf16Safe(combined, maxContextChars) + "\n...[truncated]..."
121122
: combined;
122123

123124
// When using the default section set, use precise prose that names the

0 commit comments

Comments
 (0)