fix: skip unresolved delivery targets for no-deliver cron

This commit is contained in:
Peter Steinberger
2026-04-26 08:17:16 +01:00
parent f4f74a2391
commit c5bbf83904
2 changed files with 55 additions and 13 deletions

View File

@@ -271,6 +271,28 @@ describe("runCronIsolatedAgentTurn message tool policy", () => {
});
});
it('skips implicit target resolution for bare delivery.mode "none"', async () => {
mockRunCronFallbackPassthrough();
resolveCronDeliveryPlanMock.mockReturnValue({
requested: false,
mode: "none",
});
await runCronIsolatedAgentTurn({
...makeParams(),
job: makeMessageToolPolicyJob({ mode: "none" }),
});
expect(resolveDeliveryTargetMock).not.toHaveBeenCalled();
expect(runEmbeddedPiAgentMock).toHaveBeenCalledTimes(1);
expect(runEmbeddedPiAgentMock.mock.calls[0]?.[0]).toMatchObject({
disableMessageTool: false,
forceMessageTool: true,
});
expect(runEmbeddedPiAgentMock.mock.calls[0]?.[0]?.messageChannel).toBeUndefined();
expect(runEmbeddedPiAgentMock.mock.calls[0]?.[0]?.messageTo).toBeUndefined();
});
it('suppresses automatic exec completion notifications when delivery.mode is "none"', async () => {
mockRunCronFallbackPassthrough();
resolveCronDeliveryPlanMock.mockReturnValue({
@@ -353,7 +375,7 @@ describe("runCronIsolatedAgentTurn message tool policy", () => {
});
});
it("resolves implicit last-target context for bare delivery.mode none", async () => {
it('does not resolve implicit "last" context for bare delivery.mode none', async () => {
mockRunCronFallbackPassthrough();
resolveCronDeliveryPlanMock.mockReturnValue({
requested: false,
@@ -373,19 +395,14 @@ describe("runCronIsolatedAgentTurn message tool policy", () => {
} as never,
});
expect(resolveDeliveryTargetMock).toHaveBeenCalledTimes(1);
expect(resolveDeliveryTargetMock.mock.calls[0]?.[2]).toMatchObject({
channel: "last",
sessionKey: undefined,
});
expect(resolveDeliveryTargetMock).not.toHaveBeenCalled();
expect(runEmbeddedPiAgentMock).toHaveBeenCalledTimes(1);
expect(runEmbeddedPiAgentMock.mock.calls[0]?.[0]).toMatchObject({
disableMessageTool: false,
forceMessageTool: true,
messageChannel: "messagechat",
messageTo: "123",
currentChannelId: "123",
});
expect(runEmbeddedPiAgentMock.mock.calls[0]?.[0]?.messageChannel).toBeUndefined();
expect(runEmbeddedPiAgentMock.mock.calls[0]?.[0]?.messageTo).toBeUndefined();
});
it("resolves implicit last-target context for delivery.mode none with only accountId", async () => {
@@ -690,7 +707,7 @@ describe("runCronIsolatedAgentTurn message tool policy", () => {
);
});
it("marks no-deliver runs delivered when the message tool sends to the current target", async () => {
it("does not mark bare no-deliver runs delivered when the current target is unresolved", async () => {
mockRunCronFallbackPassthrough();
resolveCronDeliveryPlanMock.mockReturnValue({
requested: false,
@@ -707,11 +724,12 @@ describe("runCronIsolatedAgentTurn message tool policy", () => {
expect(dispatchCronDeliveryMock.mock.calls[0]?.[0]).toEqual(
expect.objectContaining({
deliveryRequested: false,
skipMessagingToolDelivery: true,
skipMessagingToolDelivery: false,
unverifiedMessagingToolDelivery: true,
}),
);
expect(result.delivered).toBe(true);
expect(result.deliveryAttempted).toBe(true);
expect(result.delivered).toBe(false);
expect(result.deliveryAttempted).toBe(false);
});
});

View File

@@ -301,6 +301,12 @@ function canPromptForMessageTool(params: {
return !params.toolsAllow?.length || params.toolsAllow.includes("message");
}
function hasExplicitCronDeliveryTarget(plan: CronDeliveryPlan): boolean {
return Boolean(
(plan.channel && plan.channel !== "last") || plan.to || plan.threadId || plan.accountId,
);
}
async function resolveCronDeliveryContext(params: {
cfg: OpenClawConfig;
job: CronJob;
@@ -326,6 +332,24 @@ async function resolveCronDeliveryContext(params: {
}),
};
}
if (deliveryPlan.mode === "none" && !hasExplicitCronDeliveryTarget(deliveryPlan)) {
return {
deliveryPlan,
deliveryRequested: false,
resolvedDelivery: {
ok: false as const,
channel: undefined,
to: undefined,
accountId: undefined,
threadId: undefined,
mode: "implicit" as const,
error: new Error("delivery is disabled"),
},
toolPolicy: resolveCronToolPolicy({
deliveryMode: deliveryPlan.mode,
}),
};
}
const { resolveDeliveryTarget } = await loadCronDeliveryRuntime();
const resolvedDelivery = await resolveDeliveryTarget(params.cfg, params.agentId, {
channel: deliveryPlan.channel ?? "last",