mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-05 22:32:12 +00:00
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.
95 lines
2.6 KiB
TypeScript
95 lines
2.6 KiB
TypeScript
import type { OpenClawConfig } from "../config/types.js";
|
|
import {
|
|
type CommandNormalizeOptions,
|
|
listChatCommands,
|
|
listChatCommandsForConfig,
|
|
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,
|
|
cfg?: OpenClawConfig,
|
|
options?: CommandNormalizeOptions,
|
|
): boolean {
|
|
if (!text) {
|
|
return false;
|
|
}
|
|
const trimmed = text.trim();
|
|
if (!trimmed) {
|
|
return false;
|
|
}
|
|
const stripped = stripInboundMetadata(trimmed);
|
|
if (!stripped) {
|
|
return false;
|
|
}
|
|
const normalizedBody = normalizeCommandBody(stripped, options);
|
|
if (!normalizedBody) {
|
|
return false;
|
|
}
|
|
const lowered = normalizedBody.toLowerCase();
|
|
const commands = cfg ? listChatCommandsForConfig(cfg) : listChatCommands();
|
|
for (const command of commands) {
|
|
for (const alias of command.textAliases) {
|
|
const normalized = alias.trim().toLowerCase();
|
|
if (!normalized) {
|
|
continue;
|
|
}
|
|
if (lowered === normalized) {
|
|
return true;
|
|
}
|
|
if (command.acceptsArgs && lowered.startsWith(normalized)) {
|
|
const nextChar = normalizedBody.charAt(normalized.length);
|
|
if (nextChar && /\s/.test(nextChar)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
export function isControlCommandMessage(
|
|
text?: string,
|
|
cfg?: OpenClawConfig,
|
|
options?: CommandNormalizeOptions,
|
|
): boolean {
|
|
if (!text) {
|
|
return false;
|
|
}
|
|
const trimmed = text.trim();
|
|
if (!trimmed) {
|
|
return false;
|
|
}
|
|
if (hasControlCommand(trimmed, cfg, options)) {
|
|
return true;
|
|
}
|
|
const stripped = stripInboundMetadata(trimmed);
|
|
const normalized = normalizeCommandBody(stripped, options).trim().toLowerCase();
|
|
return isAbortTrigger(normalized);
|
|
}
|
|
|
|
/**
|
|
* Coarse detection for inline directives/shortcuts (e.g. "hey /status") so channel monitors
|
|
* can decide whether to compute CommandAuthorized for a message.
|
|
*
|
|
* This intentionally errs on the side of false positives; CommandAuthorized only gates
|
|
* command/directive execution, not normal chat replies.
|
|
*/
|
|
export function hasInlineCommandTokens(text?: string): boolean {
|
|
const body = text ?? "";
|
|
if (!body.trim()) {
|
|
return false;
|
|
}
|
|
return /(?:^|\s)[/!][a-z]/i.test(body);
|
|
}
|
|
|
|
export function shouldComputeCommandAuthorized(
|
|
text?: string,
|
|
cfg?: OpenClawConfig,
|
|
options?: CommandNormalizeOptions,
|
|
): boolean {
|
|
return isControlCommandMessage(text, cfg, options) || hasInlineCommandTokens(text);
|
|
}
|