fix(active-memory): keep recall summaries UTF-16 safe at truncation boundary#98711
fix(active-memory): keep recall summaries UTF-16 safe at truncation boundary#98711ZengWen-DT wants to merge 2 commits into
Conversation
…oundary Co-authored-by: Cursor <[email protected]>
|
Codex review: needs maintainer review before merge. Reviewed July 2, 2026, 10:06 PM ET / 02:06 UTC. Summary PR surface: Source +3, Tests +72. Total +75 across 2 files. Reproducibility: yes. for source-level reproduction: current main slices active-memory recall summaries by raw UTF-16 code units, and the read-only probe shows a dangling surrogate when an emoji straddles the cut. I did not run the full OpenClaw hook locally because this review is read-only, but the PR adds hook-level regression coverage for the path. Review metrics: none identified. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Next step before merge
Security Review detailsBest possible solution: Keep this PR open until a linked canonical PR proves it covers this PR's unique work, or a maintainer confirms closure. Do we have a high-confidence way to reproduce the issue? Yes for source-level reproduction: current main slices active-memory recall summaries by raw UTF-16 code units, and the read-only probe shows a dangling surrogate when an emoji straddles the cut. I did not run the full OpenClaw hook locally because this review is read-only, but the PR adds hook-level regression coverage for the path. Is this the best way to solve the issue? Yes: the final patch keeps the old raw-cut word-boundary decision and uses AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 3ad465d32b3b. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +3, Tests +72. Total +75 across 2 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
@clawsweeper re-review |
|
Thanks @ZengWen-DT. #100244 folded this active-memory repair into the canonical backend UTF-16 boundary fix and preserved your co-author credit. Landed on Closing this PR as superseded. A concrete active-memory truncation case not covered by the landed implementation would be grounds for a new issue or reopening. |
What Problem This Solves
Fixes an issue where the active-memory recall summary that gets prepended to the agent prompt could contain a lone UTF-16 surrogate half when the summary was long enough to be truncated and an emoji (or other astral character) sat exactly on the truncation boundary. The broken half renders as a replacement character in the injected
<active_memory_plugin>context.Why This Change Was Made
truncateSummarynow preserves the old whitespace/word-boundary decision from the raw cut, but uses the existing surrogate-safesliceUtf16Safehelper for the returned text. That keeps the previous word-boundary behavior while ensuring the reserved-ellipsis cut can no longer split a surrogate pair. This mirrors the recently merged tool-summary fix (#98644), which applied the same helper to the same class of user-visible truncation.User Impact
Recall summaries containing emoji or other astral Unicode now truncate cleanly before the split character instead of leaking a broken half-character into prompt context. Existing word-boundary truncation is preserved, including the whitespace-before-emoji boundary case raised in review.
Evidence
Premise:
String.prototype.sliceoperates on UTF-16 code units, soslice(0, n)where indexnfalls inside a surrogate pair keeps a dangling high surrogate.sliceUtf16Safe(openclaw/plugin-sdk/text-utility-runtime) is the existing accepted helper for this exact case and is already used by the merged tool-summary fix (#98644) and by sibling channels (Discord model picker, Feishu streaming card).Review finding fixed: added coverage for the whitespace-before-emoji boundary. The regression drives the real
before_prompt_buildactive-memory hook and verifiesalpha beta cccccccccccccccccccccccccc…is kept instead of falling back toalpha beta…, whileTAILWORDis not included.Full focused active-memory test file after the revision:
Real OpenClaw run proof: ran
pnpm openclaw agent --local --session-key agent:main:direct:proof3 --channel webchat --message "what should I remember about the emoji boundary?" --verbose on --json --timeout 60with an isolated temporaryOPENCLAW_STATE_DIR, active-memory enabled,maxSummaryChars: 40, and a local mock OpenAI-compatible provider. The recall subagent made a realmemory_gettool call against a temporaryMEMORY.md; the mock provider then returned the emoji-bearing recall summaryalpha beta cccccccccccccccccccccccccc 🎉TAILWORD. The main model request contained the hidden active-memory prompt prefix after truncation:Type-check, format, and lint on the changed files:
What was not tested: no external live provider credentials were used. The after-fix proof used OpenClaw's real local agent runtime, real active-memory plugin loading, a real active-memory recall subagent turn, and a local OpenAI-compatible test provider so the prompt injection behavior could be captured without sending private proof data to an external model.
AI-assisted: implemented with Cursor; all proof commands were run manually.