fix(auto-reply): keep consumed reset triggers out of prompt

This commit is contained in:
Peter Steinberger
2026-04-28 10:23:37 +01:00
parent aa2f964bda
commit 1f1b98e33b
4 changed files with 75 additions and 0 deletions

View File

@@ -14,6 +14,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- Plugins/inspector: keep bundled plugin runtime capture quiet and config-tolerant for Codex, memory-lancedb, Feishu, Mattermost, QQBot, and Tlon so plugin-inspector JSON checks can validate the full bundled set. Thanks @vincentkoc.
- Slack/auto-reply: keep fully consumed text reset triggers such as `new session` out of `BodyForAgent` after directive cleanup, so configured Slack reset phrases do not leak into the fresh model turn. Fixes #73137. Thanks @neeravmakwana.
- 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 <message>` and `/new <message>` still seed the next model turn. Fixes #73367 and #73412. Thanks @hoyanhan, @wenxu007, and @amdhelper.

View File

@@ -128,6 +128,7 @@ async function resolveHelloWithModelDefaults(params: {
groupResolution: undefined,
isGroup: false,
triggerBodyNormalized: "hello",
resetTriggered: false,
commandAuthorized: false,
defaultProvider: "openai",
defaultModel: "gpt-4o-mini",
@@ -302,6 +303,7 @@ describe("resolveReplyDirectives", () => {
groupResolution: undefined,
isGroup: false,
triggerBodyNormalized: "hello",
resetTriggered: false,
commandAuthorized: false,
defaultProvider: "openai",
defaultModel: "gpt-4o-mini",
@@ -393,6 +395,7 @@ describe("resolveReplyDirectives", () => {
groupResolution: undefined,
isGroup: false,
triggerBodyNormalized: "/trace on",
resetTriggered: false,
commandAuthorized: true,
defaultProvider: "openai",
defaultModel: "gpt-4o-mini",
@@ -461,4 +464,69 @@ describe("resolveReplyDirectives", () => {
});
expect(resolveDefaultReasoningLevel).not.toHaveBeenCalled();
});
it("keeps consumed text reset triggers empty after directive cleanup", async () => {
const sessionCtx = {
Body: "",
BodyStripped: "",
BodyForAgent: "",
BodyForCommands: "new session",
CommandBody: "new session",
Provider: "slack",
Surface: "slack",
} as TemplateContext;
const result = await resolveReplyDirectives({
ctx: buildTestCtx({
Body: "new session",
BodyForAgent: "new session",
BodyForCommands: "new session",
CommandBody: "new session",
CommandAuthorized: true,
Provider: "slack",
Surface: "slack",
}),
cfg: {
session: {
resetTriggers: ["/new", "/reset", "new session"],
},
},
agentId: "main",
agentDir: "/tmp/main-agent",
workspaceDir: "/tmp",
agentCfg: {},
sessionCtx,
sessionEntry: makeSessionEntry(),
sessionStore: {
"agent:main:slack:C123": makeSessionEntry(),
},
sessionKey: "agent:main:slack:C123",
storePath: "/tmp/sessions.json",
sessionScope: "per-sender",
groupResolution: undefined,
isGroup: false,
triggerBodyNormalized: "new session",
resetTriggered: true,
commandAuthorized: true,
defaultProvider: "openai",
defaultModel: "gpt-4o-mini",
aliasIndex: { byAlias: new Map(), byKey: new Map() },
provider: "openai",
model: "gpt-4o-mini",
hasResolvedHeartbeatModelOverride: false,
typing: makeTypingController(),
opts: undefined,
skillFilter: undefined,
});
expect(result).toEqual({
kind: "continue",
result: expect.objectContaining({
cleanedBody: "",
}),
});
expect(sessionCtx.Body).toBe("");
expect(sessionCtx.BodyForAgent).toBe("");
expect(sessionCtx.BodyStripped).toBe("");
});
});

View File

@@ -156,6 +156,7 @@ export async function resolveReplyDirectives(params: {
groupResolution: Parameters<typeof resolveGroupRequireMention>[0]["groupResolution"];
isGroup: boolean;
triggerBodyNormalized: string;
resetTriggered: boolean;
commandAuthorized: boolean;
defaultProvider: string;
defaultModel: string;
@@ -183,6 +184,7 @@ export async function resolveReplyDirectives(params: {
groupResolution,
isGroup,
triggerBodyNormalized,
resetTriggered,
commandAuthorized,
defaultProvider,
defaultModel,
@@ -335,6 +337,9 @@ export async function resolveReplyDirectives(params: {
const existingBody = sessionCtx.BodyStripped ?? sessionCtx.Body ?? "";
let cleanedBody = (() => {
if (!existingBody) {
if (resetTriggered) {
return "";
}
return parsedDirectives.cleaned;
}
if (!sessionCtx.CommandBody && !sessionCtx.RawBody) {

View File

@@ -469,6 +469,7 @@ export async function getReplyFromConfig(
groupResolution,
isGroup,
triggerBodyNormalized,
resetTriggered,
commandAuthorized,
defaultProvider,
defaultModel,