fix(cron): do not misclassify empty/NO_REPLY as interim acknowledgement (#41401)

* fix(cron): do not misclassify empty/NO_REPLY as interim acknowledgement

When a cron task's agent returns NO_REPLY, the payload filter strips the
silent token, leaving an empty text string. isLikelyInterimCronMessage()
previously returned true for empty input, causing the cron runner to
inject a forced rerun prompt ('Your previous response was only an
acknowledgement...').

Change the empty-string branch to return false: empty text after payload
filtering means the agent deliberately chose silent completion, not that
it sent an interim 'on it' message.

Fixes #41246

* fix(cron): do not misclassify empty/NO_REPLY as interim acknowledgement

Fixes #41246. (#41383) thanks @jackal092927.

---------

Co-authored-by: xaeon2026 <xaeon2026@gmail.com>
This commit is contained in:
Robin Waslander
2026-03-09 21:16:28 +01:00
committed by GitHub
parent 0bcddb3d4f
commit 2b2e5e2038
3 changed files with 11 additions and 3 deletions

View File

@@ -22,6 +22,7 @@ Docs: https://docs.openclaw.ai
- ACP/setSessionMode: propagate gateway `sessions.patch` failures back to ACP clients so rejected mode changes no longer return silent success. (#41185) thanks @pejmanjohn.
- Agents/embedded logs: add structured, sanitized lifecycle and failover observation events so overload and provider failures are easier to tail and filter. (#41336) thanks @altaywtf.
- iOS/gateway foreground recovery: reconnect immediately on foreground return after stale background sockets are torn down, so the app no longer stays disconnected until a later wake path happens. (#41384) Thanks @mbelinky.
- Cron/subagent followup: do not misclassify empty or `NO_REPLY` cron responses as interim acknowledgements that need a rerun, so deliberately silent cron jobs are no longer retried. (#41383) thanks @jackal092927.
## 2026.3.8

View File

@@ -47,8 +47,12 @@ describe("isLikelyInterimCronMessage", () => {
false,
);
});
it("treats empty as interim", () => {
expect(isLikelyInterimCronMessage("")).toBe(true);
it("does not treat empty as interim (empty = NO_REPLY was stripped)", () => {
expect(isLikelyInterimCronMessage("")).toBe(false);
});
it("does not treat whitespace-only as interim", () => {
expect(isLikelyInterimCronMessage(" ")).toBe(false);
});
});

View File

@@ -42,7 +42,10 @@ function normalizeHintText(value: string): string {
export function isLikelyInterimCronMessage(value: string): boolean {
const normalized = normalizeHintText(value);
if (!normalized) {
return true;
// Empty text after payload filtering means the agent either returned
// NO_REPLY (deliberately silent) or produced no deliverable content.
// Do not treat this as an interim acknowledgement that needs a rerun.
return false;
}
const words = normalized.split(" ").filter(Boolean).length;
return words <= 45 && INTERIM_CRON_HINTS.some((hint) => normalized.includes(hint));