mirror of
https://github.com/moltbot/moltbot.git
synced 2026-03-07 22:44:16 +00:00
fix(auto-reply): expand standalone stop phrases
This commit is contained in:
@@ -17,6 +17,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Discord/Threading: recover missing thread parent IDs by refetching thread metadata before resolving parent channel context. (#24897) Thanks @z-x-yang.
|
||||
- Web UI/i18n: load and hydrate saved locale translations during startup so non-English sessions apply immediately without manual toggling. (#24795) Thanks @chilu18.
|
||||
- Plugins/Config schema: support legacy plugin schemas without `toJSONSchema()` by falling back to permissive object schema generation. (#24933) Thanks @pandego.
|
||||
- Auto-reply/Abort shortcuts: expand standalone stop phrases (`stop openclaw`, `stop action`, `stop run`, `stop agent`, `please stop`, and related variants) and accept trailing punctuation (for example `STOP OPENCLAW!!!`) so emergency stop messages are caught more reliably.
|
||||
- Cron/Isolated sessions: use full prompt mode for isolated cron runs so skills/extensions are available during cron execution. (#24944)
|
||||
- Discord/Reasoning: suppress reasoning/thinking-only payload blocks from Discord delivery output. (#24969)
|
||||
- Sessions/Reasoning: persist `reasoningLevel: "off"` explicitly instead of deleting it so session overrides survive patch/update flows. (#24406, #24559)
|
||||
|
||||
@@ -283,7 +283,7 @@ Runtime override (owner only):
|
||||
- `openclaw gateway call sessions.list --params '{}'` — fetch sessions from the running gateway (use `--url`/`--token` for remote gateway access).
|
||||
- Send `/status` as a standalone message in chat to see whether the agent is reachable, how much of the session context is used, current thinking/verbose toggles, and when your WhatsApp web creds were last refreshed (helps spot relink needs).
|
||||
- Send `/context list` or `/context detail` to see what’s in the system prompt and injected workspace files (and the biggest context contributors).
|
||||
- Send `/stop` as a standalone message to abort the current run, clear queued followups for that session, and stop any sub-agent runs spawned from it (the reply includes the stopped count).
|
||||
- Send `/stop` (or standalone abort phrases like `stop`, `stop action`, `stop run`, `stop openclaw`) to abort the current run, clear queued followups for that session, and stop any sub-agent runs spawned from it (the reply includes the stopped count).
|
||||
- Send `/compact` (optional instructions) as a standalone message to summarize older context and free up window space. See [/concepts/compaction](/concepts/compaction).
|
||||
- JSONL transcripts can be opened directly to review full turns.
|
||||
|
||||
|
||||
@@ -2814,6 +2814,19 @@ Send any of these **as a standalone message** (no slash):
|
||||
|
||||
```
|
||||
stop
|
||||
stop action
|
||||
stop current action
|
||||
stop run
|
||||
stop current run
|
||||
stop agent
|
||||
stop the agent
|
||||
stop openclaw
|
||||
openclaw stop
|
||||
stop don't do anything
|
||||
stop do not do anything
|
||||
stop doing anything
|
||||
please stop
|
||||
stop please
|
||||
abort
|
||||
esc
|
||||
wait
|
||||
|
||||
@@ -99,7 +99,7 @@ Cron jobs panel notes:
|
||||
- `chat.inject` appends an assistant note to the session transcript and broadcasts a `chat` event for UI-only updates (no agent run, no channel delivery).
|
||||
- Stop:
|
||||
- Click **Stop** (calls `chat.abort`)
|
||||
- Type `/stop` (or `stop|esc|abort|wait|exit|interrupt`) to abort out-of-band
|
||||
- Type `/stop` (or standalone abort phrases like `stop`, `stop action`, `stop run`, `stop openclaw`, `please stop`) to abort out-of-band
|
||||
- `chat.abort` supports `{ sessionKey }` (no `runId`) to abort all active runs for that session
|
||||
- Abort partial retention:
|
||||
- When a run is aborted, partial assistant text can still be shown in the UI
|
||||
|
||||
@@ -122,25 +122,52 @@ describe("abort detection", () => {
|
||||
expect(result.triggerBodyNormalized).toBe("/stop");
|
||||
});
|
||||
|
||||
it("isAbortTrigger matches bare word triggers (without slash)", () => {
|
||||
expect(isAbortTrigger("stop")).toBe(true);
|
||||
expect(isAbortTrigger("esc")).toBe(true);
|
||||
expect(isAbortTrigger("abort")).toBe(true);
|
||||
expect(isAbortTrigger("wait")).toBe(true);
|
||||
expect(isAbortTrigger("exit")).toBe(true);
|
||||
expect(isAbortTrigger("interrupt")).toBe(true);
|
||||
it("isAbortTrigger matches standalone abort trigger phrases", () => {
|
||||
const positives = [
|
||||
"stop",
|
||||
"esc",
|
||||
"abort",
|
||||
"wait",
|
||||
"exit",
|
||||
"interrupt",
|
||||
"stop openclaw",
|
||||
"openclaw stop",
|
||||
"stop action",
|
||||
"stop current action",
|
||||
"stop run",
|
||||
"stop current run",
|
||||
"stop agent",
|
||||
"stop the agent",
|
||||
"stop don't do anything",
|
||||
"stop dont do anything",
|
||||
"stop do not do anything",
|
||||
"stop doing anything",
|
||||
"please stop",
|
||||
"stop please",
|
||||
"STOP OPENCLAW",
|
||||
"stop openclaw!!!",
|
||||
"stop don’t do anything",
|
||||
];
|
||||
for (const candidate of positives) {
|
||||
expect(isAbortTrigger(candidate)).toBe(true);
|
||||
}
|
||||
|
||||
expect(isAbortTrigger("hello")).toBe(false);
|
||||
// /stop is NOT matched by isAbortTrigger - it's handled separately
|
||||
expect(isAbortTrigger("do not do that")).toBe(false);
|
||||
// /stop is NOT matched by isAbortTrigger - it's handled separately.
|
||||
expect(isAbortTrigger("/stop")).toBe(false);
|
||||
});
|
||||
|
||||
it("isAbortRequestText aligns abort command semantics", () => {
|
||||
expect(isAbortRequestText("/stop")).toBe(true);
|
||||
expect(isAbortRequestText("/stop!!!")).toBe(true);
|
||||
expect(isAbortRequestText("stop")).toBe(true);
|
||||
expect(isAbortRequestText("stop action")).toBe(true);
|
||||
expect(isAbortRequestText("stop openclaw!!!")).toBe(true);
|
||||
expect(isAbortRequestText("/stop@openclaw_bot", { botUsername: "openclaw_bot" })).toBe(true);
|
||||
|
||||
expect(isAbortRequestText("/status")).toBe(false);
|
||||
expect(isAbortRequestText("stop please")).toBe(false);
|
||||
expect(isAbortRequestText("do not do that")).toBe(false);
|
||||
expect(isAbortRequestText("/abort")).toBe(false);
|
||||
});
|
||||
|
||||
|
||||
@@ -23,15 +23,47 @@ import type { FinalizedMsgContext, MsgContext } from "../templating.js";
|
||||
import { stripMentions, stripStructuralPrefixes } from "./mentions.js";
|
||||
import { clearSessionQueues } from "./queue.js";
|
||||
|
||||
const ABORT_TRIGGERS = new Set(["stop", "esc", "abort", "wait", "exit", "interrupt"]);
|
||||
const ABORT_TRIGGERS = new Set([
|
||||
"stop",
|
||||
"esc",
|
||||
"abort",
|
||||
"wait",
|
||||
"exit",
|
||||
"interrupt",
|
||||
"stop openclaw",
|
||||
"openclaw stop",
|
||||
"stop action",
|
||||
"stop current action",
|
||||
"stop run",
|
||||
"stop current run",
|
||||
"stop agent",
|
||||
"stop the agent",
|
||||
"stop don't do anything",
|
||||
"stop dont do anything",
|
||||
"stop do not do anything",
|
||||
"stop doing anything",
|
||||
"please stop",
|
||||
"stop please",
|
||||
]);
|
||||
const ABORT_MEMORY = new Map<string, boolean>();
|
||||
const ABORT_MEMORY_MAX = 2000;
|
||||
const TRAILING_ABORT_PUNCTUATION_RE = /[.!?…,,。;;::'"’”)\]}]+$/u;
|
||||
|
||||
function normalizeAbortTriggerText(text: string): string {
|
||||
return text
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(/[’`]/g, "'")
|
||||
.replace(/\s+/g, " ")
|
||||
.replace(TRAILING_ABORT_PUNCTUATION_RE, "")
|
||||
.trim();
|
||||
}
|
||||
|
||||
export function isAbortTrigger(text?: string): boolean {
|
||||
if (!text) {
|
||||
return false;
|
||||
}
|
||||
const normalized = text.trim().toLowerCase();
|
||||
const normalized = normalizeAbortTriggerText(text);
|
||||
return ABORT_TRIGGERS.has(normalized);
|
||||
}
|
||||
|
||||
@@ -43,7 +75,12 @@ export function isAbortRequestText(text?: string, options?: CommandNormalizeOpti
|
||||
if (!normalized) {
|
||||
return false;
|
||||
}
|
||||
return normalized.toLowerCase() === "/stop" || isAbortTrigger(normalized);
|
||||
const normalizedLower = normalized.toLowerCase();
|
||||
return (
|
||||
normalizedLower === "/stop" ||
|
||||
normalizeAbortTriggerText(normalizedLower) === "/stop" ||
|
||||
isAbortTrigger(normalizedLower)
|
||||
);
|
||||
}
|
||||
|
||||
export function getAbortMemory(key: string): boolean | undefined {
|
||||
|
||||
Reference in New Issue
Block a user