fix(cron): deliver full announce output instead of last chunk only#57322
fix(cron): deliver full announce output instead of last chunk only#57322hydro13 merged 1 commit intoopenclaw:mainfrom
Conversation
Greptile SummaryThis PR fixes a bug where
Confidence Score: 5/5Safe to merge — the fix is well-scoped, correctly handles all identified edge cases, and is backed by thorough regression tests covering multi-payload success, mixed success/error, and all-error fallback scenarios. All remaining observations are P2 or lower. The core logic in pickDeliverablePayloads is correct: successful non-error deliverables are preserved in order; when none exist, the single last-error fallback prevents notification spam. The deliveryPayloadHasStructuredContent scoping is explicitly intentional per the PR description and tested. No logic bugs, data-loss paths, or security concerns were identified. No files require special attention.
|
| Filename | Overview |
|---|---|
| src/cron/isolated-agent/helpers.ts | Extracts isDeliverablePayload helper, adds pickDeliverablePayloads returning all successful deliverable payloads with error-only fallback, and updates resolveCronPayloadOutcome to use it. Logic is correct for all identified edge cases. |
| src/cron/isolated-agent/helpers.test.ts | New pickDeliverablePayloads describe block covers multi-payload success and all-errors fallback. Tests are well-structured and cover the key scenarios. |
| src/cron/isolated-agent.helpers.test.ts | Three new integration-level tests for resolveCronPayloadOutcome: multi-payload success, structured-content scoping to last payload, and all-error fallback. All correctly reflect intended behaviour. |
| src/cron/isolated-agent.direct-delivery-forum-topics.test.ts | New integration test verifying that all successful text chunks are delivered independently to Telegram forum-topic targets (the original bug surface). Error payloads in the middle are correctly filtered. |
| src/cron/isolated-agent.skips-delivery-without-whatsapp-recipient-besteffortdeliver-true.test.ts | Test helper assertExplicitTelegramTargetDelivery updated from single expectedText to expectedTexts[], with branched assertion logic for single vs. multi-message calls. Existing tests updated to use arrays. |
Reviews (1): Last reviewed commit: "fix(cron): deliver full announce output ..." | Re-trigger Greptile
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1af36fafb8
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
a937c5b to
71d471f
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 71d471f713
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
71d471f to
a31659b
Compare
resolveCronPayloadOutcome() collapsed announce delivery to the last deliverable payload. Replace with pickDeliverablePayloads() that preserves all successful text payloads. Error-only runs fall back to the last error payload only. Extract shared isDeliverablePayload() helper. Keep deliveryPayloadHasStructuredContent scoped to the last payload to preserve downstream finalizeTextDelivery safeguards. Fixes openclaw#13812
a31659b to
779d2ce
Compare
|
Merged via squash.
|
🔒 Aisle Security AnalysisWe found 1 potential security issue(s) in this PR:
Vulnerabilities1. 🟡 Unbounded outbound message sending due to delivering all successful payloads in cron announce flow
Description
Security impact:
Vulnerable code (behavioral change enabling unbounded sends): export function pickDeliverablePayloads(payloads: DeliveryPayload[]): DeliveryPayload[] {
const successfulDeliverablePayloads = payloads.filter(
(payload) => payload != null && payload.isError !== true && isDeliverablePayload(payload),
);
if (successfulDeliverablePayloads.length > 0) {
return successfulDeliverablePayloads;
}
const lastDeliverablePayload = pickLastDeliverablePayload(payloads);
return lastDeliverablePayload ? [lastDeliverablePayload] : [];
}While payload generation is typically internal, it can often be influenced by prompts, retrieved content, tools, or upstream inputs. Without a cap, a malicious or malformed input that increases the number of produced payloads can cause outbound spam/rate-limit exhaustion and increased delivery costs. RecommendationIntroduce an explicit cap/aggregation strategy for cron announce delivery payloads. Options (pick one or combine):
const deliveryPayload = pickLastDeliverablePayload(payloads);
const deliveryPayloads = deliveryPayload ? [deliveryPayload] : [];
const MAX_ANNOUNCE_PAYLOADS = 3;
const selected = pickDeliverablePayloads(payloads).slice(-MAX_ANNOUNCE_PAYLOADS);
// Optionally merge text-only payloads
const mergedText = selected.map(p => (p.text ?? "").trim()).filter(Boolean).join("\n\n");
const deliveryPayloads = mergedText ? [{ text: mergedText }] : selected;
Also consider applying stricter rules for what counts as deliverable in announce mode (e.g., exclude interim/status updates) to reduce inadvertent flooding. Analyzed PR: #57322 at commit Last updated on: 2026-03-30T09:44:35Z |
…penclaw#57322) resolveCronPayloadOutcome() collapsed announce delivery to the last deliverable payload. Replace with pickDeliverablePayloads() that preserves all successful text payloads. Error-only runs fall back to the last error payload only. Extract shared isDeliverablePayload() helper. Keep deliveryPayloadHasStructuredContent scoped to the last payload to preserve downstream finalizeTextDelivery safeguards. Fixes openclaw#13812
…penclaw#57322) resolveCronPayloadOutcome() collapsed announce delivery to the last deliverable payload. Replace with pickDeliverablePayloads() that preserves all successful text payloads. Error-only runs fall back to the last error payload only. Extract shared isDeliverablePayload() helper. Keep deliveryPayloadHasStructuredContent scoped to the last payload to preserve downstream finalizeTextDelivery safeguards. Fixes openclaw#13812
…penclaw#57322) resolveCronPayloadOutcome() collapsed announce delivery to the last deliverable payload. Replace with pickDeliverablePayloads() that preserves all successful text payloads. Error-only runs fall back to the last error payload only. Extract shared isDeliverablePayload() helper. Keep deliveryPayloadHasStructuredContent scoped to the last payload to preserve downstream finalizeTextDelivery safeguards. Fixes openclaw#13812
…penclaw#57322) resolveCronPayloadOutcome() collapsed announce delivery to the last deliverable payload. Replace with pickDeliverablePayloads() that preserves all successful text payloads. Error-only runs fall back to the last error payload only. Extract shared isDeliverablePayload() helper. Keep deliveryPayloadHasStructuredContent scoped to the last payload to preserve downstream finalizeTextDelivery safeguards. Fixes openclaw#13812
Summary
resolveCronPayloadOutcome()collapsed announce delivery to the last deliverable payloadpickDeliverablePayloads()that preserves all successful text payloadsdeliveryPayloadHasStructuredContentstays scoped to the last payload (preserves downstream safeguards)isDeliverablePayload()helperRoot Cause
pickLastDeliverablePayload()insrc/cron/isolated-agent/helpers.tsreturned only one payload. When a cron job produced multiple text chunks, only the last survived. Telegram forum topics exposed this directly because they use the direct-delivery path.Change Type
Testing
73 test suites, 557 tests pass in
src/cron/. Typecheck (pnpm tsgo) clean. New regression tests:Fixes #13812