diff --git a/CHANGELOG.md b/CHANGELOG.md index 624d682bd78..80f79f23dc8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,7 @@ Docs: https://docs.openclaw.ai - Gateway/startup: start chat channels without waiting for primary model prewarm, keeping model warmup bounded in the background so Slack and other channels come online promptly when provider discovery is slow. Supersedes #73420. Thanks @dorukardahan. - Gateway/install: carry env-backed config SecretRefs such as `channels.discord.token` into generated service environments when they are present only in the installing shell, while keeping gateway auth SecretRefs non-persisted. Fixes #67817; supersedes #73426. Thanks @wdimaculangan and @ztexydt-cqh. -- 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. +- 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 and #73412. Thanks @hoyanhan, @wenxu007, and @amdhelper. - Auto-reply: preserve voice-note media from silent turns while continuing to suppress text and non-voice media, so `NO_REPLY` TTS replies still deliver the requested audio bubble. (#73406) Thanks @zqchris. - Channels/Mattermost: stop enqueueing regular inbound posts as system events, so Mattermost user messages reach the model only as user-role inbound-envelope content instead of also appearing as `System: Mattermost message...` directives. Fixes #71795. Thanks @juan-flores077. - 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. diff --git a/docs/gateway/config-agents.md b/docs/gateway/config-agents.md index ffd5090301d..44fc5f494c5 100644 --- a/docs/gateway/config-agents.md +++ b/docs/gateway/config-agents.md @@ -125,8 +125,9 @@ knob. `agents.defaults.bootstrapTotalMaxChars`: normal workspace bootstrap injection. - `agents.defaults.startupContext.*`: - one-shot `/new` and `/reset` startup prelude, including recent daily - `memory/*.md` files. + one-shot reset/startup model-run prelude, including recent daily + `memory/*.md` files. Bare chat `/new` and `/reset` commands are + acknowledged without invoking the model. - `skills.limits.*`: the compact skills list injected into the system prompt. - `agents.defaults.contextLimits.*`: @@ -142,8 +143,9 @@ budget: #### `agents.defaults.startupContext` -Controls the first-turn startup prelude injected on bare `/new` and `/reset` -runs. +Controls the first-turn startup prelude injected on reset/startup model runs. +Bare chat `/new` and `/reset` commands acknowledge the reset without invoking +the model, so they do not load this prelude. ```json5 { diff --git a/docs/reference/token-use.md b/docs/reference/token-use.md index ab233f5ffda..50661b7e29f 100644 --- a/docs/reference/token-use.md +++ b/docs/reference/token-use.md @@ -21,7 +21,7 @@ OpenClaw assembles its own system prompt on every run. It includes: with optional per-agent override at `agents.list[].skillsLimits.maxSkillsPromptChars`. - Self-update instructions -- Workspace + bootstrap files (`AGENTS.md`, `SOUL.md`, `TOOLS.md`, `IDENTITY.md`, `USER.md`, `HEARTBEAT.md`, `BOOTSTRAP.md` when new, plus `MEMORY.md` when present). Lowercase root `memory.md` is not injected; it is legacy repair input for `openclaw doctor --fix` when paired with `MEMORY.md`. Large files are truncated by `agents.defaults.bootstrapMaxChars` (default: 12000), and total bootstrap injection is capped by `agents.defaults.bootstrapTotalMaxChars` (default: 60000). `memory/*.md` daily files are not part of the normal bootstrap prompt; they remain on-demand via memory tools on ordinary turns, but bare `/new` and `/reset` can prepend a one-shot startup-context block with recent daily memory for that first turn. That startup prelude is controlled by `agents.defaults.startupContext`. +- Workspace + bootstrap files (`AGENTS.md`, `SOUL.md`, `TOOLS.md`, `IDENTITY.md`, `USER.md`, `HEARTBEAT.md`, `BOOTSTRAP.md` when new, plus `MEMORY.md` when present). Lowercase root `memory.md` is not injected; it is legacy repair input for `openclaw doctor --fix` when paired with `MEMORY.md`. Large files are truncated by `agents.defaults.bootstrapMaxChars` (default: 12000), and total bootstrap injection is capped by `agents.defaults.bootstrapTotalMaxChars` (default: 60000). `memory/*.md` daily files are not part of the normal bootstrap prompt; they remain on-demand via memory tools on ordinary turns, but reset/startup model runs can prepend a one-shot startup-context block with recent daily memory for that first turn. Bare chat `/new` and `/reset` commands are acknowledged without invoking the model. The startup prelude is controlled by `agents.defaults.startupContext`. - Time (UTC + user timezone) - Reply tags + heartbeat behavior - Runtime metadata (host/OS/model/thinking) diff --git a/docs/start/openclaw.md b/docs/start/openclaw.md index 740da0fbb83..ca1c847994a 100644 --- a/docs/start/openclaw.md +++ b/docs/start/openclaw.md @@ -159,7 +159,7 @@ Example: - Session files: `~/.openclaw/agents//sessions/{{SessionId}}.jsonl` - Session metadata (token usage, last route, etc): `~/.openclaw/agents//sessions/sessions.json` (legacy: `~/.openclaw/sessions/sessions.json`) -- `/new` or `/reset` starts a fresh session for that chat (configurable via `resetTriggers`). If sent alone, the agent replies with a short hello to confirm the reset. +- `/new` or `/reset` starts a fresh session for that chat (configurable via `resetTriggers`). If sent alone, OpenClaw acknowledges the reset without invoking the model. - `/compact [instructions]` compacts the session context and reports the remaining context budget. ## Heartbeats (proactive mode) diff --git a/src/auto-reply/reply.triggers.trigger-handling.targets-active-session-native-stop.e2e.test.ts b/src/auto-reply/reply.triggers.trigger-handling.targets-active-session-native-stop.e2e.test.ts index d5415a3fdfb..8486273dc8c 100644 --- a/src/auto-reply/reply.triggers.trigger-handling.targets-active-session-native-stop.e2e.test.ts +++ b/src/auto-reply/reply.triggers.trigger-handling.targets-active-session-native-stop.e2e.test.ts @@ -11,7 +11,7 @@ import { makeCfg, mockRunEmbeddedPiAgentOk, requireSessionStorePath, - runGreetingPromptForBareNewOrReset, + expectBareNewOrResetAcknowledged, withTempHome, } from "../../test/helpers/auto-reply/trigger-handling-test-harness.js"; import { loadSessionStore, resolveSessionKey } from "../config/sessions.js"; @@ -375,7 +375,7 @@ describe("trigger handling", () => { }); }); - it("prepends runtime-loaded daily memory context on bare /new", async () => { + it("acknowledges bare /new without invoking the model or loading startup memory", async () => { await withTempHome(async (home) => { const workspaceDir = join(home, "openclaw"); const nowMs = Date.now(); @@ -392,18 +392,12 @@ describe("trigger handling", () => { const res = await runAuthorizedSmsCommand("/new", cfg); - expect(maybeReplyText(res)).toBe("hello"); - const prompt = runEmbeddedPiAgentMock.mock.calls.at(-1)?.[0]?.prompt ?? ""; - expect(prompt).toContain("[Startup context loaded by runtime]"); - expect(prompt).toContain(`[Untrusted daily memory: memory/${todayStamp}.md]`); - expect(prompt).toContain("BEGIN_QUOTED_NOTES"); - expect(prompt).toContain("today startup note"); - expect(prompt).toContain(`[Untrusted daily memory: memory/${yesterdayStamp}.md]`); - expect(prompt).toContain("yesterday startup note"); + expect(maybeReplyText(res)).toBe("✅ New session started."); + expect(runEmbeddedPiAgentMock).not.toHaveBeenCalled(); }); }); - it("treats normalized /RESET as reset for startupContext.applyOn", async () => { + it("acknowledges normalized bare /RESET without invoking the model", async () => { await withTempHome(async (home) => { const workspaceDir = join(home, "openclaw"); const nowMs = Date.now(); @@ -418,10 +412,8 @@ describe("trigger handling", () => { const res = await runAuthorizedSmsCommand("/RESET", cfg); - expect(maybeReplyText(res)).toBe("hello"); - const prompt = runEmbeddedPiAgentMock.mock.calls.at(-1)?.[0]?.prompt ?? ""; - expect(prompt).toContain(`[Untrusted daily memory: memory/${todayStamp}.md]`); - expect(prompt).toContain("reset startup note"); + expect(maybeReplyText(res)).toBe("✅ Session reset."); + expect(runEmbeddedPiAgentMock).not.toHaveBeenCalled(); }); }); @@ -824,7 +816,7 @@ describe("trigger handling", () => { it("handles bare session reset, inline commands, and unauthorized inline status", async () => { await withTempHome(async (home) => { - await runGreetingPromptForBareNewOrReset({ home, body: "/new", getReplyFromConfig }); + await expectBareNewOrResetAcknowledged({ home, body: "/new", getReplyFromConfig }); await expectResetBlockedForNonOwner({ home }); await expectInlineCommandHandledAndStripped({ home, diff --git a/test/helpers/auto-reply/trigger-handling-test-harness.ts b/test/helpers/auto-reply/trigger-handling-test-harness.ts index 2058acdef94..f83f8ed6423 100644 --- a/test/helpers/auto-reply/trigger-handling-test-harness.ts +++ b/test/helpers/auto-reply/trigger-handling-test-harness.ts @@ -414,7 +414,7 @@ export async function expectInlineCommandHandledAndStripped(params: { expect(text).toBe("ok"); } -export async function runGreetingPromptForBareNewOrReset(params: { +export async function expectBareNewOrResetAcknowledged(params: { home: string; body: "/new" | "/reset"; getReplyFromConfig: typeof import("../../../src/auto-reply/reply.js").getReplyFromConfig; @@ -440,12 +440,8 @@ export async function runGreetingPromptForBareNewOrReset(params: { makeCfg(params.home), ); const text = Array.isArray(res) ? res[0]?.text : res?.text; - expect(text).toBe("hello"); - expect(runEmbeddedPiAgentMock).toHaveBeenCalledOnce(); - const prompt = runEmbeddedPiAgentMock.mock.calls.at(-1)?.[0]?.prompt ?? ""; - expect(prompt).toContain("A new session was started via /new or /reset"); - expect(prompt).toContain("Execute your Session Startup sequence now"); - expect(prompt).toContain("read the required files before responding to the user"); + expect(text).toBe(params.body === "/reset" ? "✅ Session reset." : "✅ New session started."); + expect(runEmbeddedPiAgentMock).not.toHaveBeenCalled(); } export function installTriggerHandlingE2eTestHooks() {