Merge 8151abb0ad84aef44b443e32ab4b68449f2d6548 into 8a05c05596ca9ba0735dafd8e359885de4c2c969
This commit is contained in:
commit
cec1533d45
@ -467,4 +467,59 @@ describe("dispatchCronDelivery — double-announce guard", () => {
|
||||
vi.unstubAllEnvs();
|
||||
}
|
||||
});
|
||||
|
||||
it("suppresses NO_REPLY payload in direct delivery so sentinel never leaks to external channels", async () => {
|
||||
vi.mocked(countActiveDescendantRuns).mockReturnValue(0);
|
||||
vi.mocked(isLikelyInterimCronMessage).mockReturnValue(false);
|
||||
|
||||
const params = makeBaseParams({ synthesizedText: "NO_REPLY" });
|
||||
// Force the useDirectDelivery path (structured content) to exercise
|
||||
// deliverViaDirect without going through finalizeTextDelivery.
|
||||
(params as Record<string, unknown>).deliveryPayloadHasStructuredContent = true;
|
||||
const state = await dispatchCronDelivery(params);
|
||||
|
||||
// NO_REPLY must be filtered out before reaching the outbound adapter.
|
||||
expect(deliverOutboundPayloads).not.toHaveBeenCalled();
|
||||
// Mark as silently delivered so the job is persisted as successful.
|
||||
expect(state.delivered).toBe(true);
|
||||
// deliveryAttempted must be true so the heartbeat timer does not fire
|
||||
// a fallback enqueueSystemEvent with the NO_REPLY sentinel text.
|
||||
expect(state.deliveryAttempted).toBe(true);
|
||||
|
||||
// Verify timer guard agrees: shouldEnqueueCronMainSummary returns false
|
||||
expect(
|
||||
shouldEnqueueCronMainSummary({
|
||||
summaryText: "NO_REPLY",
|
||||
deliveryRequested: true,
|
||||
delivered: state.delivered,
|
||||
deliveryAttempted: state.deliveryAttempted,
|
||||
suppressMainSummary: false,
|
||||
isCronSystemEvent: () => true,
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("suppresses NO_REPLY payload with surrounding whitespace", async () => {
|
||||
vi.mocked(countActiveDescendantRuns).mockReturnValue(0);
|
||||
vi.mocked(isLikelyInterimCronMessage).mockReturnValue(false);
|
||||
|
||||
const params = makeBaseParams({ synthesizedText: " NO_REPLY " });
|
||||
(params as Record<string, unknown>).deliveryPayloadHasStructuredContent = true;
|
||||
const state = await dispatchCronDelivery(params);
|
||||
|
||||
expect(deliverOutboundPayloads).not.toHaveBeenCalled();
|
||||
expect(state.delivered).toBe(true);
|
||||
expect(state.deliveryAttempted).toBe(true);
|
||||
|
||||
expect(
|
||||
shouldEnqueueCronMainSummary({
|
||||
summaryText: " NO_REPLY ",
|
||||
deliveryRequested: true,
|
||||
delivered: state.delivered,
|
||||
deliveryAttempted: state.deliveryAttempted,
|
||||
suppressMainSummary: false,
|
||||
isCronSystemEvent: () => true,
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { countActiveDescendantRuns } from "../../agents/subagent-registry.js";
|
||||
import { SILENT_REPLY_TOKEN } from "../../auto-reply/tokens.js";
|
||||
import { isSilentReplyText, SILENT_REPLY_TOKEN } from "../../auto-reply/tokens.js";
|
||||
import type { ReplyPayload } from "../../auto-reply/types.js";
|
||||
import { createOutboundSendDeps, type CliDeps } from "../../cli/outbound-send-deps.js";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
@ -331,14 +331,28 @@ export async function dispatchCronDelivery(
|
||||
delivery,
|
||||
});
|
||||
try {
|
||||
const payloadsForDelivery =
|
||||
const rawPayloads =
|
||||
deliveryPayloads.length > 0
|
||||
? deliveryPayloads
|
||||
: synthesizedText
|
||||
? [{ text: synthesizedText }]
|
||||
: [];
|
||||
// Suppress NO_REPLY sentinel so it never leaks to external channels.
|
||||
const payloadsForDelivery = rawPayloads.filter(
|
||||
(p) => !isSilentReplyText(p.text, SILENT_REPLY_TOKEN),
|
||||
);
|
||||
if (payloadsForDelivery.length === 0) {
|
||||
return null;
|
||||
// Mark as silently delivered so the heartbeat timer does not fire a fallback
|
||||
// and the job is persisted as successfully delivered.
|
||||
deliveryAttempted = true;
|
||||
delivered = true;
|
||||
return params.withRunSession({
|
||||
status: "ok",
|
||||
summary,
|
||||
outputText,
|
||||
delivered: true,
|
||||
...params.telemetry,
|
||||
});
|
||||
}
|
||||
if (params.isAborted()) {
|
||||
return params.withRunSession({
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user