From 5497662408a626e87ccc2bb5f5a98cd7223bfa99 Mon Sep 17 00:00:00 2001
From: lzyyzznl
Date: Thu, 9 Jul 2026 19:41:38 +0800
Subject: [PATCH] fix(active-memory): keep search queries UTF-16 safe (#102621)
Co-authored-by: Peter Steinberger
---
extensions/active-memory/index.test.ts | 25 +++++++++++++++++++++++++
extensions/active-memory/index.ts | 2 +-
2 files changed, 26 insertions(+), 1 deletion(-)
diff --git a/extensions/active-memory/index.test.ts b/extensions/active-memory/index.test.ts
index 16863c882ba1..9f27b53f36b4 100644
--- a/extensions/active-memory/index.test.ts
+++ b/extensions/active-memory/index.test.ts
@@ -5097,6 +5097,31 @@ describe("active-memory plugin", () => {
expect(prompt).not.toContain("Recent conversation tail:");
});
+ it("keeps a whole code point when the bounded search query crosses an emoji", async () => {
+ api.pluginConfig = {
+ agents: ["main"],
+ queryMode: "message",
+ };
+ plugin.register(api as unknown as OpenClawPluginApi);
+ const prefix = "a".repeat(479);
+
+ await hooks.before_prompt_build(
+ {
+ prompt: `${prefix}😀tail`,
+ messages: [],
+ },
+ {
+ agentId: "main",
+ trigger: "user",
+ sessionKey: "agent:main:main",
+ messageProvider: "webchat",
+ },
+ );
+
+ const query = lastEmbeddedPrompt().match(/Bounded memory search query:\n([^\n]*)/u)?.[1];
+ expect(query).toBe(prefix);
+ });
+
it("sends a bounded latest-message query instead of channel metadata to memory search", async () => {
api.pluginConfig = {
agents: ["main"],
diff --git a/extensions/active-memory/index.ts b/extensions/active-memory/index.ts
index a06458ff3d4c..221b68828cfb 100644
--- a/extensions/active-memory/index.ts
+++ b/extensions/active-memory/index.ts
@@ -2711,7 +2711,7 @@ function normalizeSearchQueryText(text: string): string {
function clampSearchQuery(text: string): string {
const normalized = text.replace(/\s+/g, " ").trim();
return normalized.length > MAX_ACTIVE_MEMORY_SEARCH_QUERY_CHARS
- ? normalized.slice(0, MAX_ACTIVE_MEMORY_SEARCH_QUERY_CHARS).trim()
+ ? truncateUtf16Safe(normalized, MAX_ACTIVE_MEMORY_SEARCH_QUERY_CHARS).trim()
: normalized;
}