Skip to content

Commit 5434769

Browse files
committed
fix(cron): suppress source replies for announce delivery
1 parent 428fc16 commit 5434769

4 files changed

Lines changed: 50 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ Docs: https://docs.openclaw.ai
3131
- Mac app: keep app-level menu commands and Dashboard failure states reachable when the remote Gateway is disconnected, and keep the Settings sidebar toggle in the leading titlebar area.
3232
- Gateway/webchat: hide internal runtime-context and other `display: false` transcript messages from Chat history and live message events. Fixes #83216. Thanks @EmpireCreator.
3333
- CLI/help: keep `gateway`, `doctor`, `status`, and `health` help registration out of action/runtime imports so subcommand `--help` stays lightweight in constrained terminals. Fixes #83228. Thanks @dfguerrerom.
34+
- Cron/Discord: keep explicit announce runs in message-tool-only source-reply mode so scheduled agent turns post once instead of also echoing through automatic visible replies. Fixes #83261. Thanks @Theralley.
3435
- Mac app: align the Sessions settings pane with the standard Settings page gutter and row spacing.
3536
- OpenAI/Codex: stop rejecting available `openai-codex` GPT-5.1, GPT-5.2, and GPT-5.3 model refs during config validation, while keeping removed Spark aliases suppressed. Fixes #83303.
3637
- Codex app-server: preserve streamed native command output in mirrored transcripts and trajectory exports when final snapshots omit aggregated output. (#83200) Thanks @rozmiarD.

src/cron/isolated-agent/run-executor.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { BootstrapContextMode } from "../../agents/bootstrap-files.js";
22
import { resolveCliRuntimeExecutionProvider } from "../../agents/model-runtime-aliases.js";
33
import type { SkillSnapshot } from "../../agents/skills.js";
44
import { normalizeToolList } from "../../agents/tool-policy.js";
5+
import type { SourceReplyDeliveryMode } from "../../auto-reply/get-reply-options.types.js";
56
import type { ThinkLevel, VerboseLevel } from "../../auto-reply/thinking.js";
67
import type { AgentDefaultsConfig } from "../../config/types.agent-defaults.js";
78
import type { OpenClawConfig } from "../../config/types.openclaw.js";
@@ -116,6 +117,7 @@ export function createCronPromptExecutor(params: {
116117
to?: string;
117118
threadId?: string | number;
118119
};
120+
sourceReplyDeliveryMode?: SourceReplyDeliveryMode;
119121
toolPolicy: {
120122
requireExplicitMessageTarget: boolean;
121123
disableMessageTool: boolean;
@@ -202,6 +204,7 @@ export function createCronPromptExecutor(params: {
202204
cliSessionId,
203205
skillsSnapshot: params.skillsSnapshot,
204206
messageChannel: params.messageChannel,
207+
sourceReplyDeliveryMode: params.sourceReplyDeliveryMode,
205208
abortSignal: params.abortSignal,
206209
onExecutionStarted: params.onExecutionStarted,
207210
onExecutionPhase: params.onExecutionPhase,
@@ -273,6 +276,7 @@ export function createCronPromptExecutor(params: {
273276
notifyOnExitEmptySuccess: false,
274277
}
275278
: undefined,
279+
sourceReplyDeliveryMode: params.sourceReplyDeliveryMode,
276280
runId: params.cronSession.sessionEntry.sessionId,
277281
requireExplicitMessageTarget: params.toolPolicy.requireExplicitMessageTarget,
278282
disableMessageTool: params.toolPolicy.disableMessageTool,
@@ -326,6 +330,7 @@ export async function executeCronRun(params: {
326330
to?: string;
327331
threadId?: string | number;
328332
};
333+
sourceReplyDeliveryMode?: SourceReplyDeliveryMode;
329334
toolPolicy: {
330335
requireExplicitMessageTarget: boolean;
331336
disableMessageTool: boolean;
@@ -380,6 +385,7 @@ export async function executeCronRun(params: {
380385
messageChannel: params.resolvedDelivery.channel,
381386
suppressExecNotifyOnExit: params.suppressExecNotifyOnExit,
382387
resolvedDelivery: params.resolvedDelivery,
388+
sourceReplyDeliveryMode: params.sourceReplyDeliveryMode,
383389
toolPolicy: params.toolPolicy,
384390
skillsSnapshot: params.skillsSnapshot,
385391
agentPayload: params.agentPayload,

src/cron/isolated-agent/run.message-tool-policy.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,25 @@ describe("runCronIsolatedAgentTurn message tool policy", () => {
565565
});
566566
});
567567

568+
it("keeps cron announce source replies message-tool-only", async () => {
569+
mockRunCronFallbackPassthrough();
570+
resolveCronDeliveryPlanMock.mockReturnValue(makeAnnounceDeliveryPlan());
571+
572+
await runCronIsolatedAgentTurn({
573+
...makeParams(),
574+
job: makeAnnounceMessageToolJob(),
575+
});
576+
577+
expect(runEmbeddedPiAgentMock).toHaveBeenCalledTimes(1);
578+
expectEmbeddedRunFields({
579+
sourceReplyDeliveryMode: "message_tool_only",
580+
forceMessageTool: true,
581+
messageChannel: "messagechat",
582+
messageTo: "123",
583+
currentChannelId: "123",
584+
});
585+
});
586+
568587
it("keeps automatic exec completion notifications when announce delivery is active", async () => {
569588
mockRunCronFallbackPassthrough();
570589
resolveCronDeliveryPlanMock.mockReturnValue(makeAnnounceDeliveryPlan());

src/cron/isolated-agent/run.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { listOpenAIAuthProfileProvidersForAgentRuntime } from "../../agents/open
44
import { retireSessionMcpRuntime } from "../../agents/pi-bundle-mcp-tools.js";
55
import type { MessagingToolSend } from "../../agents/pi-embedded-messaging.types.js";
66
import type { SkillSnapshot } from "../../agents/skills.js";
7+
import type { SourceReplyDeliveryMode } from "../../auto-reply/get-reply-options.types.js";
78
import type { ThinkLevel } from "../../auto-reply/thinking.js";
89
import type { CliDeps } from "../../cli/outbound-send-deps.js";
910
import type { AgentDefaultsConfig } from "../../config/types.agent-defaults.js";
@@ -326,6 +327,21 @@ function resolveCronToolPolicy(params: { deliveryMode: "announce" | "webhook" |
326327
};
327328
}
328329

330+
function resolveCronSourceReplyDeliveryMode(params: {
331+
deliveryPlan: CronDeliveryPlan;
332+
resolvedDelivery: ResolvedCronDeliveryTarget;
333+
toolPolicy: ReturnType<typeof resolveCronToolPolicy>;
334+
}): SourceReplyDeliveryMode | undefined {
335+
if (
336+
params.deliveryPlan.mode !== "announce" ||
337+
params.toolPolicy.disableMessageTool ||
338+
!params.resolvedDelivery.ok
339+
) {
340+
return undefined;
341+
}
342+
return "message_tool_only";
343+
}
344+
329345
function canPromptForMessageTool(params: {
330346
disableMessageTool: boolean;
331347
toolsAllow?: string[];
@@ -470,6 +486,7 @@ type PreparedCronRunContext = {
470486
deliveryPlan: CronDeliveryPlan;
471487
resolvedDelivery: ResolvedCronDeliveryTarget;
472488
deliveryRequested: boolean;
489+
sourceReplyDeliveryMode?: SourceReplyDeliveryMode;
473490
suppressExecNotifyOnExit: boolean;
474491
senderIsOwner: boolean;
475492
toolPolicy: ReturnType<typeof resolveCronToolPolicy>;
@@ -701,6 +718,11 @@ async function prepareCronRunContext(params: {
701718
job: input.job,
702719
agentId,
703720
});
721+
const sourceReplyDeliveryMode = resolveCronSourceReplyDeliveryMode({
722+
deliveryPlan,
723+
resolvedDelivery,
724+
toolPolicy,
725+
});
704726

705727
const { formattedTime, timeLine } = resolveCronStyleNow(input.cfg, now);
706728
const base = `[cron:${input.job.id} ${input.job.name}] ${input.message}`.trim();
@@ -833,6 +855,7 @@ async function prepareCronRunContext(params: {
833855
deliveryPlan,
834856
resolvedDelivery,
835857
deliveryRequested,
858+
sourceReplyDeliveryMode,
836859
suppressExecNotifyOnExit: deliveryPlan.mode === "none",
837860
senderIsOwner: !isExternalHook,
838861
toolPolicy,
@@ -1179,6 +1202,7 @@ export async function runCronIsolatedAgentTurn(params: {
11791202
accountId: prepared.context.resolvedDelivery.accountId,
11801203
threadId: prepared.context.resolvedDelivery.threadId,
11811204
},
1205+
sourceReplyDeliveryMode: prepared.context.sourceReplyDeliveryMode,
11821206
toolPolicy: prepared.context.toolPolicy,
11831207
skillsSnapshot: prepared.context.skillsSnapshot,
11841208
agentPayload: prepared.context.agentPayload,

0 commit comments

Comments
 (0)