From 75d659787c9eda31635ac2aafd0a61f79d2f03c4 Mon Sep 17 00:00:00 2001 From: zhi Date: Fri, 27 Feb 2026 14:37:59 +0000 Subject: [PATCH] fix(rules): strip trailing metadata blocks before checking end symbol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit getLastChar was checking the last character of the full event.prompt, which includes Conversation/Sender metadata blocks appended by OpenClaw after the actual message. This meant end symbols like 🔚 at the end of the message body were invisible — the last char was always backtick or whitespace from the metadata JSON block. Fix: strip trailing '(untrusted metadata)' blocks before extracting the last character. This only affects non-humanList senders (humanList senders bypass end symbol check via human_list_sender reason). --- plugin/rules.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/plugin/rules.ts b/plugin/rules.ts index ccaacd8..f886f56 100644 --- a/plugin/rules.ts +++ b/plugin/rules.ts @@ -25,8 +25,24 @@ export type Decision = { reason: string; }; +/** + * Strip trailing OpenClaw metadata blocks from the prompt to get the actual message content. + * The prompt format is: [prepend instruction]\n\n[message]\n\nConversation info (untrusted metadata):\n```json\n{...}\n```\n\nSender (untrusted metadata):\n```json\n{...}\n``` + */ +function stripTrailingMetadata(input: string): string { + // Remove all trailing "XXX (untrusted metadata):\n```json\n...\n```" blocks + let text = input; + // eslint-disable-next-line no-constant-condition + while (true) { + const m = text.match(/\n*[A-Z][^\n]*\(untrusted metadata\):\s*```json\s*[\s\S]*?```\s*$/); + if (!m) break; + text = text.slice(0, text.length - m[0].length); + } + return text; +} + function getLastChar(input: string): string { - const t = input.trim(); + const t = stripTrailingMetadata(input).trim(); return t.length ? t[t.length - 1] : ""; }