diff --git a/CHANGELOG.md b/CHANGELOG.md index 429de870f97..a2001be2869 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Docs: https://docs.openclaw.ai ### Fixes +- Auto-reply/commands: stop bare `/reset` and `/new` after reset hooks acknowledge the command, so non-ACP channels no longer fall through into empty provider calls while `/reset ` and `/new ` still seed the next model turn. Fixes #73367. Thanks @hoyanhan and @wenxu007. - Agents/Anthropic: send implicit Anthropic beta headers only to direct public Anthropic endpoints, including OAuth, so custom Anthropic-compatible providers no longer mis-handle unsupported beta flags unless explicitly configured. Refs #73346. Thanks @byBrodowski. - Skills: require explicit `skills.entries.coding-agent.enabled` before exposing the bundled coding-agent skill, so installs with Codex on PATH but no OpenAI auth do not silently offer Codex delegation. Fixes #73358. Thanks @LaFleurAdvertising and @Sanjays2402. - Plugins/startup: precompute bundled runtime mirror fingerprints before taking the mirror lock, including dist-runtime canonical roots, so Docker Desktop/WSL cold starts no longer hold `.openclaw-runtime-mirror.lock` while scanning slow persisted volumes. Fixes #73339. Thanks @1yihui. diff --git a/src/auto-reply/reply/commands-reset-hooks.test.ts b/src/auto-reply/reply/commands-reset-hooks.test.ts index 8ac9c06014e..1b1b5809923 100644 --- a/src/auto-reply/reply/commands-reset-hooks.test.ts +++ b/src/auto-reply/reply/commands-reset-hooks.test.ts @@ -430,4 +430,52 @@ describe("handleCommands reset hooks", () => { expect(triggerInternalHookMock).not.toHaveBeenCalled(); expect(resetMocks.resetConfiguredBindingTargetInPlace).not.toHaveBeenCalled(); }); + + it("acknowledges bare /reset without falling through to model execution", async () => { + const params = buildResetParams("/reset", { + commands: { text: true }, + channels: { whatsapp: { allowFrom: ["*"] } }, + } as OpenClawConfig); + + const result = await maybeHandleResetCommand(params); + + expect(result).toEqual({ + shouldContinue: false, + reply: { text: "✅ Session reset." }, + }); + expect(triggerInternalHookMock).toHaveBeenCalledWith( + expect.objectContaining({ type: "command", action: "reset" }), + ); + }); + + it("acknowledges bare /new without falling through to model execution", async () => { + const params = buildResetParams("/new", { + commands: { text: true }, + channels: { whatsapp: { allowFrom: ["*"] } }, + } as OpenClawConfig); + + const result = await maybeHandleResetCommand(params); + + expect(result).toEqual({ + shouldContinue: false, + reply: { text: "✅ New session started." }, + }); + expect(triggerInternalHookMock).toHaveBeenCalledWith( + expect.objectContaining({ type: "command", action: "new" }), + ); + }); + + it("keeps reset tails falling through so the model receives the user input", async () => { + const params = buildResetParams("/new take notes", { + commands: { text: true }, + channels: { whatsapp: { allowFrom: ["*"] } }, + } as OpenClawConfig); + + const result = await maybeHandleResetCommand(params); + + expect(result).toBeNull(); + expect(triggerInternalHookMock).toHaveBeenCalledWith( + expect.objectContaining({ type: "command", action: "new" }), + ); + }); }); diff --git a/src/auto-reply/reply/commands-reset.ts b/src/auto-reply/reply/commands-reset.ts index 920a7ebcebf..7f856220826 100644 --- a/src/auto-reply/reply/commands-reset.ts +++ b/src/auto-reply/reply/commands-reset.ts @@ -166,5 +166,13 @@ export async function maybeHandleResetCommand( previousSessionEntry: params.previousSessionEntry, workspaceDir: params.workspaceDir, }); + if (!resetTail) { + return { + shouldContinue: false, + reply: { + text: commandAction === "reset" ? "✅ Session reset." : "✅ New session started.", + }, + }; + } return null; }