fix(agents): avoid empty memory flush prompts

This commit is contained in:
Peter Steinberger
2026-05-02 07:13:45 +01:00
parent 8ed05b6ab6
commit 5e35112d21
6 changed files with 8 additions and 7 deletions

View File

@@ -27,6 +27,7 @@ Docs: https://docs.openclaw.ai
- Agents/sandbox: preserve existing workspace file modes when sandbox edits atomically replace files, so 0644 files do not collapse to 0600 after Write/Edit/apply_patch. Fixes #44077. Thanks @patosullivan.
- Agents/models: keep legacy CLI runtime model refs such as `claude-cli/*` in the configured allowlist after canonical runtime migration, so cron `payload.model` overrides keep working. Fixes #75753. Thanks @RyanSandoval.
- Gateway/watch: keep colored subsystem log prefixes in the managed tmux pane even when the parent shell exports `NO_COLOR`, while preserving explicit `FORCE_COLOR=0` opt-out. Thanks @vincentkoc.
- Agents/compaction: submit a non-empty runtime-event marker for pre-compaction memory flush turns, so strict Anthropic providers no longer reject the silent flush as an empty user message. Fixes #75305. Thanks @sableassistant3777-source.
- Plugin SDK: re-export `isPrivateIpAddress` from `plugin-sdk/ssrf-runtime`, restoring source-checkout builds for SearXNG and Firecrawl private-network guards. Thanks @vincentkoc.
- CLI/directory: report unsupported directory operations for installed channel plugins instead of prompting to reinstall the plugin when it lacks a directory adapter. Fixes #75770. Thanks @lawong888.
- Web search/SearXNG: show the JSON API `search.formats` prerequisite during SearXNG setup before prompting for the base URL. Supersedes #65592. Thanks @evanpaul14.

View File

@@ -111,7 +111,7 @@ describe("resolvePromptSubmissionSkipReason", () => {
).toBeNull();
});
it("allows blank prompt on runtimeOnly turns", () => {
it("skips blank prompt on runtimeOnly turns", () => {
expect(
resolvePromptSubmissionSkipReason({
prompt: "",
@@ -119,7 +119,7 @@ describe("resolvePromptSubmissionSkipReason", () => {
runtimeOnly: true,
imageCount: 0,
}),
).toBeNull();
).toBe("empty_prompt_history_images");
});
it("treats undefined runtimeOnly as a visible user submission", () => {

View File

@@ -244,9 +244,6 @@ export function resolvePromptSubmissionSkipReason(params: {
imageCount: number;
runtimeOnly?: boolean;
}): PromptSubmissionSkipReason | null {
if (params.runtimeOnly) {
return null;
}
if (params.prompt.trim().length > 0 || params.imageCount > 0) {
return null;
}

View File

@@ -55,7 +55,7 @@ describe("runtime context prompt submission", () => {
transcriptPrompt: "",
}),
).toEqual({
prompt: "",
prompt: "Continue the OpenClaw runtime event.",
runtimeContext: "internal event",
runtimeOnly: true,
runtimeSystemContext: expect.stringContaining("internal event"),

View File

@@ -6,6 +6,8 @@ import {
} from "../../internal-runtime-context.js";
export { OPENCLAW_RUNTIME_CONTEXT_CUSTOM_TYPE };
const OPENCLAW_RUNTIME_EVENT_USER_PROMPT = "Continue the OpenClaw runtime event.";
type RuntimeContextSession = {
sendCustomMessage: (
message: {
@@ -54,7 +56,7 @@ export function resolveRuntimeContextPromptParts(params: {
if (!prompt) {
return runtimeContext
? {
prompt: "",
prompt: OPENCLAW_RUNTIME_EVENT_USER_PROMPT,
runtimeContext,
runtimeOnly: true,
runtimeSystemContext: buildRuntimeEventSystemContext(runtimeContext),

View File

@@ -160,6 +160,7 @@ describe("runMemoryFlushIfNeeded", () => {
};
expect(flushCall.prompt).toContain("Pre-compaction memory flush.");
expect(flushCall.transcriptPrompt).toBe("");
expect(flushCall.prompt).not.toBe(flushCall.transcriptPrompt);
expect(flushCall.memoryFlushWritePath).toMatch(/^memory\/\d{4}-\d{2}-\d{2}\.md$/);
expect(flushCall.silentExpected).toBe(true);
expect(refreshQueuedFollowupSessionMock).toHaveBeenCalledWith({