Skip to content

fix(active-memory): keep recall summaries UTF-16 safe at truncation boundary#98711

Closed
ZengWen-DT wants to merge 2 commits into
openclaw:mainfrom
ZengWen-DT:fix/active-memory-summary-utf16
Closed

fix(active-memory): keep recall summaries UTF-16 safe at truncation boundary#98711
ZengWen-DT wants to merge 2 commits into
openclaw:mainfrom
ZengWen-DT:fix/active-memory-summary-utf16

Conversation

@ZengWen-DT

@ZengWen-DT ZengWen-DT commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

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

truncateSummary now preserves the old whitespace/word-boundary decision from the raw cut, but uses the existing surrogate-safe sliceUtf16Safe helper 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.slice operates on UTF-16 code units, so slice(0, n) where index n falls 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_build active-memory hook and verifies alpha beta cccccccccccccccccccccccccc… is kept instead of falling back to alpha beta…, while TAILWORD is not included.

node scripts/run-vitest.mjs run extensions/active-memory/index.test.ts -t "truncates recall summaries|keeps the old word boundary"
Test Files  1 passed (1)
Tests  2 passed | 154 skipped (156)

Full focused active-memory test file after the revision:

node scripts/run-vitest.mjs run extensions/active-memory/index.test.ts
Test Files  1 passed (1)
Tests  156 passed (156)

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 60 with an isolated temporary OPENCLAW_STATE_DIR, active-memory enabled, maxSummaryChars: 40, and a local mock OpenAI-compatible provider. The recall subagent made a real memory_get tool call against a temporary MEMORY.md; the mock provider then returned the emoji-bearing recall summary alpha beta cccccccccccccccccccccccccc 🎉TAILWORD. The main model request contained the hidden active-memory prompt prefix after truncation:

request entries: 3
recall tool call requested memory_get: true
recall tool output present: true
main prompt prefix: alpha beta cccccccccccccccccccccccccc…
main prefix has TAILWORD: false
main prefix has replacement char: false
main prefix length: 38

Type-check, format, and lint on the changed files:

pnpm format:check -- extensions/active-memory/index.ts extensions/active-memory/index.test.ts
All matched files use the correct format.

OPENCLAW_OXLINT_SKIP_PREPARE=1 node scripts/run-oxlint.mjs extensions/active-memory/index.ts extensions/active-memory/index.test.ts
exit 0

pnpm tsgo:extensions:test
exit 0

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.

@clawsweeper

clawsweeper Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed July 2, 2026, 10:06 PM ET / 02:06 UTC.

Summary
The PR imports sliceUtf16Safe into active-memory summary truncation and adds hook-level regression tests for surrogate-boundary and whitespace-before-emoji truncation cases.

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: 🦞 diamond lobster
Proof: 🦞 diamond lobster
Patch quality: 🦞 diamond lobster
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Next step before merge

  • No ClawSweeper repair job is needed; the remaining action is ordinary maintainer review and merge decision for the contributor branch.

Security
Cleared: The diff only changes active-memory text truncation and tests, imports an existing SDK helper, and does not alter dependencies, package scripts, secrets, CI, or privileged runtime behavior.

Review details

Best 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 sliceUtf16Safe only for returned text, which fixes the UTF-16 boundary bug without adding a new config surface or duplicating a generic truncation helper.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 3ad465d32b3b.

Label changes

Label changes:

  • add proof: sufficient: Contributor real behavior proof is sufficient. The PR body now includes copied terminal output from an after-fix OpenClaw agent run with active-memory enabled, a temporary state dir, a real recall subagent turn, and captured prompt-prefix output showing no replacement character or leaked tail word.
  • add rating: 🦞 diamond lobster: Overall readiness is 🦞 diamond lobster; proof is 🦞 diamond lobster and patch quality is 🦞 diamond lobster.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The PR body now includes copied terminal output from an after-fix OpenClaw agent run with active-memory enabled, a temporary state dir, a real recall subagent turn, and captured prompt-prefix output showing no replacement character or leaked tail word.
  • remove rating: 🦪 silver shellfish: Current PR rating is rating: 🦞 diamond lobster, so this older rating label is no longer current.
  • remove status: 📣 needs proof: Current PR status label is status: 👀 ready for maintainer look.

Label justifications:

  • P3: This is a narrow active-memory text-truncation correctness fix with limited blast radius and no evidence of urgent workflow breakage.
  • rating: 🦞 diamond lobster: Overall readiness is 🦞 diamond lobster; proof is 🦞 diamond lobster and patch quality is 🦞 diamond lobster.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The PR body now includes copied terminal output from an after-fix OpenClaw agent run with active-memory enabled, a temporary state dir, a real recall subagent turn, and captured prompt-prefix output showing no replacement character or leaked tail word.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body now includes copied terminal output from an after-fix OpenClaw agent run with active-memory enabled, a temporary state dir, a real recall subagent turn, and captured prompt-prefix output showing no replacement character or leaked tail word.
Evidence reviewed

PR surface:

Source +3, Tests +72. Total +75 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 6 3 +3
Tests 1 72 0 +72
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 78 3 +75

What I checked:

  • PR close coverage proof: PR close coverage proof kept this PR open against fix: tool summaries preserve emoji truncation boundaries #98644: PR B is a merged sibling fix for the same bug class, but it does not carry PR A's material active-memory recall summary behavior. The source report identifies remaining current-main work in the active-memory path, so PR A should not be closed as covered by PR B.
  • linked superseding PR: fix: tool summaries preserve emoji truncation boundaries #98644 (fix: tool summaries preserve emoji truncation boundaries) is merged at 2026-07-01T15:56:41Z.
  • cluster evidence: the durable review links that PR in the work cluster or recommended risk path.
  • no human follow-up: live comments and timeline hydrated by apply contain no non-automation activity after the ClawSweeper review.

Likely related people:

  • Takhoffman: Authored and merged the Active Memory recall plugin and the later move of recall summaries into the hidden prompt prefix, which is the runtime surface this PR changes. (role: feature owner; confidence: high; commits: b83726d13e33, f94d6778b1d5; files: extensions/active-memory/index.ts, extensions/active-memory/index.test.ts)
  • ZengWen-DT: Authored the merged sibling UTF-16 truncation fix for tool-description summaries, the same bug class this active-memory PR mirrors. (role: recent adjacent contributor; confidence: medium; commits: 85572835ae46, d9d6108086da; files: src/agents/tool-description-summary.ts, src/agents/tool-description-summary.test.ts)
  • vincentkoc: Merged the related UTF-16-safe tool-summary PR that established the nearby accepted helper-based fix pattern. (role: merger of adjacent fix; confidence: medium; commits: d9d6108086da; files: src/agents/tool-description-summary.ts, src/agents/tool-description-summary.test.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

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 keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P3 Low-priority cleanup, docs, polish, ergonomics, or speculative work. labels Jul 1, 2026
@ZengWen-DT

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Jul 2, 2026
@ZengWen-DT

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jul 3, 2026
@vincentkoc

Copy link
Copy Markdown
Member

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 main in 66081c0, including the active-memory implementation and focused regression coverage. Exact-head CI passed at https://github.com/openclaw/openclaw/actions/runs/28741038571.

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.

@vincentkoc vincentkoc closed this Jul 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P3 Low-priority cleanup, docs, polish, ergonomics, or speculative work. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. size: S status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants