fix(shared): keep assistant identity value truncation UTF-16 safe#102802
fix(shared): keep assistant identity value truncation UTF-16 safe#102802wangyan2026 wants to merge 1 commit into
Conversation
coerceIdentityValue used raw slice(0, maxLength) to truncate assistant identity labels. When the truncation boundary falls inside a UTF-16 surrogate pair (emoji, CJK extended), the resulting string contains a dangling surrogate that renders as U+FFFD. Replace slice(0, maxLength) with truncateUtf16Safe from @openclaw/normalization-core/utf16-slice so the truncation point always falls on a complete code-point boundary.
|
Closing this as another isolated UTF truncation micro-change. The production change is a two-line helper substitution, while the test also changes the separate negative- Assistant identity truncation should be handled as a bounded owner-level cleanup with the valid length contract made explicit, not as another one-call-site PR. |
|
The owner-level cleanup requested here has now landed via #103034 in Instead of preserving Validation at the exact reviewed head included green hosted CI and CodeQL, 42 sanitized AWS focused assertions, source-blind short-name/UTF-8 Gateway RPC proof, and clean fresh autoreview. Thanks @wangyan2026 for surfacing the boundary bug; the later contributor's original fix credit is preserved in the landed history. |
What Problem This Solves
coerceIdentityValue()inassistant-identity-values.tsuses rawString.prototype.slice(0, maxLength)to truncate assistant identity labels. When the truncation boundary falls inside a UTF-16 surrogate pair (emoji, CJK extended), the resulting string contains a dangling surrogate that renders as U+FFFD (�) in user-visible assistant names and metadata.Why This Change Was Made
Replace
trimmed.slice(0, maxLength)withtruncateUtf16Safe(trimmed, maxLength)from@openclaw/normalization-core/utf16-sliceso the truncation point always falls on a complete code-point boundary. This preserves the existing code-unit cap while avoiding broken emoji in assistant identity values.User Impact
maxLengthnow returns an empty string instead ofslice(0, -1)(more correct for a truncation limit)Evidence
node scripts/run-vitest.mjs src/shared/assistant-identity-values.test.ts— 5 tests passedpnpm exec oxfmt --check— passedpnpm exec oxlint— 0 warnings, 0 errorsgit diff --check— passedcoerceIdentityValue("OpenClaw🚀🚀🚀🚀🚀", 11)returns"OpenClaw🚀"(not"OpenClaw🚀\ud83d"with a dangling surrogate)