fix: strip inbound metadata before slash command detection (#58674)

Slash commands like /model and /new were silently ignored when the inbound
message body included metadata prefix blocks (Conversation info, Sender info,
timestamps) injected by buildInboundUserContextPrefix. The command detection
functions (hasControlCommand, isControlCommandMessage, parseSendPolicyCommand)
now call stripInboundMetadata before normalizeCommandBody so embedded slash
commands are correctly recognized.
This commit is contained in:
HansY 2026-04-01 04:03:15 +00:00 committed by Peter Steinberger
parent fb28b02540
commit 3b1f8e3461
No known key found for this signature in database
3 changed files with 47 additions and 3 deletions

View File

@ -877,4 +877,40 @@ describe("control command parsing", () => {
}),
).toBe(true);
});
it("detects commands wrapped in inbound metadata blocks", () => {
const metaWrapped = [
"Conversation info (untrusted metadata):",
"```json",
'{"message_id":"msg-abc","chat_id":"chat-123"}',
"```",
"",
"/model spark",
].join("\n");
expect(hasControlCommand(metaWrapped)).toBe(true);
});
it("detects /new command after metadata prefix", () => {
const metaWrapped = [
"Sender (untrusted metadata):",
"```json",
'{"name":"Alice","id":"user-1"}',
"```",
"",
"/new spark",
].join("\n");
expect(hasControlCommand(metaWrapped)).toBe(true);
});
it("detects /status command after timestamp + metadata prefix", () => {
const metaWrapped = [
"[Wed 2026-03-11 23:51 PDT] Conversation info (untrusted metadata):",
"```json",
'{"chat_id":"chat-123"}',
"```",
"",
"/status",
].join("\n");
expect(hasControlCommand(metaWrapped)).toBe(true);
});
});

View File

@ -6,6 +6,7 @@ import {
normalizeCommandBody,
} from "./commands-registry.js";
import { isAbortTrigger } from "./reply/abort-primitives.js";
import { stripInboundMetadata } from "./reply/strip-inbound-meta.js";
export function hasControlCommand(
text?: string,
@ -19,7 +20,11 @@ export function hasControlCommand(
if (!trimmed) {
return false;
}
const normalizedBody = normalizeCommandBody(trimmed, options);
const stripped = stripInboundMetadata(trimmed);
if (!stripped) {
return false;
}
const normalizedBody = normalizeCommandBody(stripped, options);
if (!normalizedBody) {
return false;
}
@ -60,7 +65,8 @@ export function isControlCommandMessage(
if (hasControlCommand(trimmed, cfg, options)) {
return true;
}
const normalized = normalizeCommandBody(trimmed, options).trim().toLowerCase();
const stripped = stripInboundMetadata(trimmed);
const normalized = normalizeCommandBody(stripped, options).trim().toLowerCase();
return isAbortTrigger(normalized);
}

View File

@ -1,4 +1,5 @@
import { normalizeCommandBody } from "./commands-registry.js";
import { stripInboundMetadata } from "./reply/strip-inbound-meta.js";
export type SendPolicyOverride = "allow" | "deny";
@ -27,7 +28,8 @@ export function parseSendPolicyCommand(raw?: string): {
if (!trimmed) {
return { hasCommand: false };
}
const normalized = normalizeCommandBody(trimmed);
const stripped = stripInboundMetadata(trimmed);
const normalized = normalizeCommandBody(stripped);
const match = normalized.match(/^\/send(?:\s+([a-zA-Z]+))?\s*$/i);
if (!match) {
return { hasCommand: false };