mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-05 04:41:35 +00:00
* fix(agents,skills): keep session-identity and experience-review transcript truncation UTF-16 safe normalizeCliContextValue (cli-output.ts) and formatSkillExperienceReviewTranscript (experience-review-prompt.ts) both used raw .slice(0, N) / .slice(-N) to truncate user-facing strings. When a truncation boundary falls inside a surrogate pair (emoji), the resulting string contains a lone surrogate — encodeURIComponent throws URIError, JSON serialisers may reject it, and downstream display surfaces can render garbage. Replace .slice(0, 200) with truncateUtf16Safe in normalizeCliContextValue. Replace .slice(0, 6_000) with truncateUtf16Safe and full.slice(-tailBudget) with sliceUtf16Safe(full, tailStart) in formatSkillExperienceReviewTranscript. Follows the pattern established by #98644, #102470, #101728 and the broader UTF-16 safety sweep already applied across the codebase. * chore: re-trigger CI after PR body update * test(agents,skills): construct transcripts exceeding 60k chars, verify old slices dangle Both experience-review fixtures now exceed EXPERIENCE_REVIEW_MAX_TRANSCRIPT_CHARS so the truncation branch actually fires. Pre-condition assertions prove the old raw .slice(0,6000) / .slice(tailStart) produce isolated surrogates; the production functions (formatSkillExperienceReviewTranscript, formatCliOutputError) do not. cli-output test also adds pre-condition check showing normalizeCliContextValue with raw .slice(0,200) would split a surrogate pair. * test: tighten UTF-16 truncation coverage Co-authored-by: 毛宇豪0668001457 <mao.yuhao@xydigit.com> --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
23 lines
746 B
TypeScript
23 lines
746 B
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { formatCliOutputError } from "./cli-output.js";
|
|
|
|
function hasDanglingSurrogate(value: string): boolean {
|
|
return /[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/u.test(value);
|
|
}
|
|
|
|
describe("formatCliOutputError", () => {
|
|
it("keeps truncated session identity UTF-16 safe", () => {
|
|
const sessionId = `${"s".repeat(199)}😀tail`;
|
|
expect(hasDanglingSurrogate(sessionId.slice(0, 200))).toBe(true);
|
|
|
|
const error = formatCliOutputError({
|
|
text: "",
|
|
sessionId,
|
|
terminalFailure: { reason: "max_turns" },
|
|
});
|
|
|
|
expect(hasDanglingSurrogate(error)).toBe(false);
|
|
expect(error).toContain(`Claude session: ${"s".repeat(199)}.`);
|
|
});
|
|
});
|