mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-07 04:01:37 +00:00
feat(cron): introduce delivery modes for isolated jobs
- Added support for new delivery modes in cron jobs: `announce`, `deliver`, and `none`. - Updated documentation to reflect changes in delivery options and usage examples. - Enhanced the cron job schema to include delivery configuration. - Refactored related CLI commands and UI components to accommodate the new delivery settings. - Improved handling of legacy delivery fields for backward compatibility. This update allows users to choose how output from isolated jobs is delivered, enhancing flexibility in job management.
This commit is contained in:
committed by
Peter Steinberger
parent
3a03e38378
commit
511c656cbc
@@ -31,6 +31,10 @@ import {
|
||||
import { runEmbeddedPiAgent } from "../../agents/pi-embedded.js";
|
||||
import { buildWorkspaceSkillSnapshot } from "../../agents/skills.js";
|
||||
import { getSkillsSnapshotVersion } from "../../agents/skills/refresh.js";
|
||||
import {
|
||||
runSubagentAnnounceFlow,
|
||||
type SubagentRunOutcome,
|
||||
} from "../../agents/subagent-announce.js";
|
||||
import { resolveAgentTimeoutMs } from "../../agents/timeout.js";
|
||||
import { hasNonzeroUsage } from "../../agents/usage.js";
|
||||
import { ensureAgentWorkspace } from "../../agents/workspace.js";
|
||||
@@ -41,7 +45,11 @@ import {
|
||||
supportsXHighThinking,
|
||||
} from "../../auto-reply/thinking.js";
|
||||
import { createOutboundSendDeps, type CliDeps } from "../../cli/outbound-send-deps.js";
|
||||
import { resolveSessionTranscriptPath, updateSessionStore } from "../../config/sessions.js";
|
||||
import {
|
||||
resolveAgentMainSessionKey,
|
||||
resolveSessionTranscriptPath,
|
||||
updateSessionStore,
|
||||
} from "../../config/sessions.js";
|
||||
import { registerAgentRunContext } from "../../infra/agent-events.js";
|
||||
import { deliverOutboundPayloads } from "../../infra/outbound/deliver.js";
|
||||
import { getRemoteSkillEligibility } from "../../infra/skills-remote.js";
|
||||
@@ -53,6 +61,7 @@ import {
|
||||
getHookType,
|
||||
isExternalHookSession,
|
||||
} from "../../security/external-content.js";
|
||||
import { resolveCronDeliveryPlan } from "../delivery.js";
|
||||
import { resolveDeliveryTarget } from "./delivery-target.js";
|
||||
import {
|
||||
isHeartbeatOnlyResponse,
|
||||
@@ -231,16 +240,15 @@ export async function runCronIsolatedAgentTurn(params: {
|
||||
});
|
||||
|
||||
const agentPayload = params.job.payload.kind === "agentTurn" ? params.job.payload : null;
|
||||
const deliveryMode =
|
||||
agentPayload?.deliver === true ? "explicit" : agentPayload?.deliver === false ? "off" : "auto";
|
||||
const hasExplicitTarget = Boolean(agentPayload?.to && agentPayload.to.trim());
|
||||
const deliveryRequested =
|
||||
deliveryMode === "explicit" || (deliveryMode === "auto" && hasExplicitTarget);
|
||||
const bestEffortDeliver = agentPayload?.bestEffortDeliver === true;
|
||||
const deliveryPlan = resolveCronDeliveryPlan(params.job);
|
||||
const deliveryRequested = deliveryPlan.requested;
|
||||
const bestEffortDeliver = deliveryPlan.bestEffort;
|
||||
const legacyDeliveryMode =
|
||||
deliveryPlan.source === "payload" ? deliveryPlan.legacyMode : undefined;
|
||||
|
||||
const resolvedDelivery = await resolveDeliveryTarget(cfgWithAgentDefaults, agentId, {
|
||||
channel: agentPayload?.channel ?? "last",
|
||||
to: agentPayload?.to,
|
||||
channel: deliveryPlan.channel ?? "last",
|
||||
to: deliveryPlan.to,
|
||||
});
|
||||
|
||||
const userTimezone = resolveUserTimezone(params.cfg.agents?.defaults?.userTimezone);
|
||||
@@ -424,7 +432,7 @@ export async function runCronIsolatedAgentTurn(params: {
|
||||
const skipHeartbeatDelivery = deliveryRequested && isHeartbeatOnlyResponse(payloads, ackMaxChars);
|
||||
const skipMessagingToolDelivery =
|
||||
deliveryRequested &&
|
||||
deliveryMode === "auto" &&
|
||||
legacyDeliveryMode === "auto" &&
|
||||
runResult.didSendViaMessagingTool === true &&
|
||||
(runResult.messagingToolSentTargets ?? []).some((target) =>
|
||||
matchesMessagingToolDeliveryTarget(target, {
|
||||
@@ -435,38 +443,70 @@ export async function runCronIsolatedAgentTurn(params: {
|
||||
);
|
||||
|
||||
if (deliveryRequested && !skipHeartbeatDelivery && !skipMessagingToolDelivery) {
|
||||
if (!resolvedDelivery.to) {
|
||||
const reason =
|
||||
resolvedDelivery.error?.message ?? "Cron delivery requires a recipient (--to).";
|
||||
if (!bestEffortDeliver) {
|
||||
if (deliveryPlan.mode === "announce") {
|
||||
const requesterSessionKey = resolveAgentMainSessionKey({
|
||||
cfg: cfgWithAgentDefaults,
|
||||
agentId,
|
||||
});
|
||||
const useExplicitOrigin = deliveryPlan.channel !== "last" || Boolean(deliveryPlan.to?.trim());
|
||||
const requesterOrigin = useExplicitOrigin
|
||||
? {
|
||||
channel: resolvedDelivery.channel,
|
||||
to: resolvedDelivery.to,
|
||||
accountId: resolvedDelivery.accountId,
|
||||
threadId: resolvedDelivery.threadId,
|
||||
}
|
||||
: undefined;
|
||||
const outcome: SubagentRunOutcome = { status: "ok" };
|
||||
const taskLabel = params.job.name?.trim() || "cron job";
|
||||
await runSubagentAnnounceFlow({
|
||||
childSessionKey: agentSessionKey,
|
||||
childRunId: cronSession.sessionEntry.sessionId,
|
||||
requesterSessionKey,
|
||||
requesterOrigin,
|
||||
requesterDisplayKey: requesterSessionKey,
|
||||
task: taskLabel,
|
||||
timeoutMs: 30_000,
|
||||
cleanup: "keep",
|
||||
roundOneReply: outputText ?? summary,
|
||||
waitForCompletion: false,
|
||||
label: `Cron: ${taskLabel}`,
|
||||
outcome,
|
||||
});
|
||||
} else {
|
||||
if (!resolvedDelivery.to) {
|
||||
const reason =
|
||||
resolvedDelivery.error?.message ?? "Cron delivery requires a recipient (--to).";
|
||||
if (!bestEffortDeliver) {
|
||||
return {
|
||||
status: "error",
|
||||
summary,
|
||||
outputText,
|
||||
error: reason,
|
||||
};
|
||||
}
|
||||
return {
|
||||
status: "error",
|
||||
summary,
|
||||
status: "skipped",
|
||||
summary: `Delivery skipped (${reason}).`,
|
||||
outputText,
|
||||
error: reason,
|
||||
};
|
||||
}
|
||||
return {
|
||||
status: "skipped",
|
||||
summary: `Delivery skipped (${reason}).`,
|
||||
outputText,
|
||||
};
|
||||
}
|
||||
try {
|
||||
await deliverOutboundPayloads({
|
||||
cfg: cfgWithAgentDefaults,
|
||||
channel: resolvedDelivery.channel,
|
||||
to: resolvedDelivery.to,
|
||||
accountId: resolvedDelivery.accountId,
|
||||
payloads,
|
||||
bestEffort: bestEffortDeliver,
|
||||
deps: createOutboundSendDeps(params.deps),
|
||||
});
|
||||
} catch (err) {
|
||||
if (!bestEffortDeliver) {
|
||||
return { status: "error", summary, outputText, error: String(err) };
|
||||
try {
|
||||
await deliverOutboundPayloads({
|
||||
cfg: cfgWithAgentDefaults,
|
||||
channel: resolvedDelivery.channel,
|
||||
to: resolvedDelivery.to,
|
||||
accountId: resolvedDelivery.accountId,
|
||||
payloads,
|
||||
bestEffort: bestEffortDeliver,
|
||||
deps: createOutboundSendDeps(params.deps),
|
||||
});
|
||||
} catch (err) {
|
||||
if (!bestEffortDeliver) {
|
||||
return { status: "error", summary, outputText, error: String(err) };
|
||||
}
|
||||
return { status: "ok", summary, outputText };
|
||||
}
|
||||
return { status: "ok", summary, outputText };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user