From 9b99787c31972a75a7cd2779b234f23fc7519c05 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 8 Mar 2026 00:31:30 +0000 Subject: [PATCH] refactor(cron): extract delivery tool policy helpers --- src/cron/isolated-agent/run.ts | 72 ++++++++++++++++++++++++++-------- 1 file changed, 55 insertions(+), 17 deletions(-) diff --git a/src/cron/isolated-agent/run.ts b/src/cron/isolated-agent/run.ts index fe8e9b86f40..45ff9f9386b 100644 --- a/src/cron/isolated-agent/run.ts +++ b/src/cron/isolated-agent/run.ts @@ -142,6 +142,54 @@ function buildCronAgentDefaultsConfig(params: { }); } +type ResolvedCronDeliveryTarget = Awaited>; + +function resolveCronToolPolicy(params: { + deliveryRequested: boolean; + resolvedDelivery: ResolvedCronDeliveryTarget; +}) { + return { + // Only enforce an explicit message target when the cron delivery target + // was successfully resolved. When resolution fails the agent should not + // be blocked by a target it cannot satisfy (#27898). + requireExplicitMessageTarget: params.deliveryRequested && params.resolvedDelivery.ok, + disableMessageTool: params.deliveryRequested, + }; +} + +async function resolveCronDeliveryContext(params: { + cfg: OpenClawConfig; + job: CronJob; + agentId: string; +}) { + const deliveryPlan = resolveCronDeliveryPlan(params.job); + const resolvedDelivery = await resolveDeliveryTarget(params.cfg, params.agentId, { + channel: deliveryPlan.channel ?? "last", + to: deliveryPlan.to, + accountId: deliveryPlan.accountId, + sessionKey: params.job.sessionKey, + }); + return { + deliveryPlan, + deliveryRequested: deliveryPlan.requested, + resolvedDelivery, + toolPolicy: resolveCronToolPolicy({ + deliveryRequested: deliveryPlan.requested, + resolvedDelivery, + }), + }; +} + +function appendCronDeliveryInstruction(params: { + commandBody: string; + deliveryRequested: boolean; +}) { + if (!params.deliveryRequested) { + return params.commandBody; + } + return `${params.commandBody}\n\nReturn your summary as plain text; it will be delivered automatically. If the task explicitly calls for messaging a specific external recipient, note who/where it should go instead of sending it yourself.`.trim(); +} + export async function runCronIsolatedAgentTurn(params: { cfg: OpenClawConfig; deps: CliDeps; @@ -373,14 +421,10 @@ export async function runCronIsolatedAgentTurn(params: { }); const agentPayload = params.job.payload.kind === "agentTurn" ? params.job.payload : null; - const deliveryPlan = resolveCronDeliveryPlan(params.job); - const deliveryRequested = deliveryPlan.requested; - - const resolvedDelivery = await resolveDeliveryTarget(cfgWithAgentDefaults, agentId, { - channel: deliveryPlan.channel ?? "last", - to: deliveryPlan.to, - accountId: deliveryPlan.accountId, - sessionKey: params.job.sessionKey, + const { deliveryRequested, resolvedDelivery, toolPolicy } = await resolveCronDeliveryContext({ + cfg: cfgWithAgentDefaults, + job: params.job, + agentId, }); const { formattedTime, timeLine } = resolveCronStyleNow(params.cfg, now); @@ -422,10 +466,7 @@ export async function runCronIsolatedAgentTurn(params: { // Internal/trusted source - use original format commandBody = `${base}\n${timeLine}`.trim(); } - if (deliveryRequested) { - commandBody = - `${commandBody}\n\nReturn your summary as plain text; it will be delivered automatically. If the task explicitly calls for messaging a specific external recipient, note who/where it should go instead of sending it yourself.`.trim(); - } + commandBody = appendCronDeliveryInstruction({ commandBody, deliveryRequested }); const existingSkillsSnapshot = cronSession.sessionEntry.skillsSnapshot; const skillsSnapshot = resolveCronSkillsSnapshot({ @@ -566,11 +607,8 @@ export async function runCronIsolatedAgentTurn(params: { bootstrapContextMode: agentPayload?.lightContext ? "lightweight" : undefined, bootstrapContextRunKind: "cron", runId: cronSession.sessionEntry.sessionId, - // Only enforce an explicit message target when the cron delivery target - // was successfully resolved. When resolution fails the agent should not - // be blocked by a target it cannot satisfy (#27898). - requireExplicitMessageTarget: deliveryRequested && resolvedDelivery.ok, - disableMessageTool: deliveryRequested, + requireExplicitMessageTarget: toolPolicy.requireExplicitMessageTarget, + disableMessageTool: toolPolicy.disableMessageTool, allowTransientCooldownProbe: runOptions?.allowTransientCooldownProbe, abortSignal, bootstrapPromptWarningSignaturesSeen,