mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-30 11:46:26 +00:00
fix(cron): restore direct fallback after announce failure in best-effort mode (openclaw#36177)
Verified: - pnpm build - pnpm check (fails on pre-existing origin/main lint debt in extensions/mattermost imports) - pnpm test:macmini Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
This commit is contained in:
@@ -32,6 +32,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Security/dependency audit: patch transitive Hono vulnerabilities by pinning `hono` to `4.12.5` and `@hono/node-server` to `1.19.10` in production resolution paths. Thanks @shakkernerd.
|
||||
- Security/dependency audit: bump `tar` to `7.5.10` (from `7.5.9`) to address the high-severity hardlink path traversal advisory (`GHSA-qffp-2rhf-9h96`). Thanks @shakkernerd.
|
||||
- Cron/announce delivery robustness: bypass pending-descendant announce guards for cron completion sends, ensure named-agent announce routes have outbound session entries, and fall back to direct delivery only when an announce send was actually attempted and failed. (from #35185, #32443, #34987) Thanks @Sid-Qin, @scoootscooob, and @bmendonca3.
|
||||
- Cron/announce best-effort fallback: run direct outbound fallback after attempted announce failures even when delivery is configured as best-effort, so Telegram cron sends are not left as attempted-but-undelivered after `cron announce delivery failed` warnings.
|
||||
- Auto-reply/system events: restore runtime system events to the message timeline (`System:` lines), preserve think-hint parsing with prepended events, and carry events into deferred followup/collect/steer-backlog prompts to keep cache behavior stable without dropping queued metadata. (#34794) Thanks @anisoptera.
|
||||
- Security/audit account handling: avoid prototype-chain account IDs in audit validation by using own-property checks for `accounts`. (#34982) Thanks @HOYALIM.
|
||||
- Cron/restart catch-up semantics: replay interrupted recurring jobs and missed immediate cron slots on startup without replaying interrupted one-shot jobs, with guarded missed-slot probing to avoid malformed-schedule startup aborts and duplicate-trigger drift after restart. (from #34466, #34896, #34625, #33206) Thanks @dunamismax, @dsantoreis, @Octane0411, and @Sid-Qin.
|
||||
|
||||
@@ -421,13 +421,13 @@ describe("runCronIsolatedAgentTurn", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("marks attempted when announce delivery reports false and best-effort is enabled", async () => {
|
||||
it("falls back to direct delivery when announce reports false and best-effort is enabled", async () => {
|
||||
const { res, deps } = await runAnnounceFlowResult(true);
|
||||
expect(res.status).toBe("ok");
|
||||
expect(res.delivered).toBe(false);
|
||||
expect(res.delivered).toBe(true);
|
||||
expect(res.deliveryAttempted).toBe(true);
|
||||
expect(runSubagentAnnounceFlow).toHaveBeenCalledTimes(1);
|
||||
expect(deps.sendMessageTelegram).not.toHaveBeenCalled();
|
||||
expect(deps.sendMessageTelegram).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("falls back to direct delivery when announce flow throws and best-effort is disabled", async () => {
|
||||
|
||||
@@ -465,39 +465,38 @@ export async function dispatchCronDelivery(
|
||||
}
|
||||
} else {
|
||||
const announceResult = await deliverViaAnnounce(params.resolvedDelivery);
|
||||
if (announceResult) {
|
||||
// Fall back to direct delivery only when the announce send was
|
||||
// actually attempted and failed. Early returns from
|
||||
// deliverViaAnnounce (active subagents, interim suppression,
|
||||
// SILENT_REPLY_TOKEN) are intentional suppressions that must NOT
|
||||
// trigger direct delivery — doing so would bypass the suppression
|
||||
// guard and leak partial/stale content to the channel. (#32432)
|
||||
if (announceDeliveryWasAttempted && !delivered && !params.isAborted()) {
|
||||
const directFallback = await deliverViaDirect(params.resolvedDelivery);
|
||||
if (directFallback) {
|
||||
return {
|
||||
result: directFallback,
|
||||
delivered,
|
||||
deliveryAttempted,
|
||||
summary,
|
||||
outputText,
|
||||
synthesizedText,
|
||||
deliveryPayloads,
|
||||
};
|
||||
}
|
||||
// If direct delivery succeeded (returned null without error),
|
||||
// `delivered` has been set to true by deliverViaDirect.
|
||||
if (delivered) {
|
||||
return {
|
||||
delivered,
|
||||
deliveryAttempted,
|
||||
summary,
|
||||
outputText,
|
||||
synthesizedText,
|
||||
deliveryPayloads,
|
||||
};
|
||||
}
|
||||
// Fall back to direct delivery only when the announce send was actually
|
||||
// attempted and failed. Early returns from deliverViaAnnounce (active
|
||||
// subagents, interim suppression, SILENT_REPLY_TOKEN) are intentional
|
||||
// suppressions that must NOT trigger direct delivery — doing so would
|
||||
// bypass the suppression guard and leak partial/stale content.
|
||||
if (announceDeliveryWasAttempted && !delivered && !params.isAborted()) {
|
||||
const directFallback = await deliverViaDirect(params.resolvedDelivery);
|
||||
if (directFallback) {
|
||||
return {
|
||||
result: directFallback,
|
||||
delivered,
|
||||
deliveryAttempted,
|
||||
summary,
|
||||
outputText,
|
||||
synthesizedText,
|
||||
deliveryPayloads,
|
||||
};
|
||||
}
|
||||
// If direct delivery succeeded (returned null without error),
|
||||
// `delivered` has been set to true by deliverViaDirect.
|
||||
if (delivered) {
|
||||
return {
|
||||
delivered,
|
||||
deliveryAttempted,
|
||||
summary,
|
||||
outputText,
|
||||
synthesizedText,
|
||||
deliveryPayloads,
|
||||
};
|
||||
}
|
||||
}
|
||||
if (announceResult) {
|
||||
return {
|
||||
result: announceResult,
|
||||
delivered,
|
||||
|
||||
Reference in New Issue
Block a user