feat(cron): default isolated jobs to announce delivery and enhance scheduling options

- Updated isolated cron jobs to default to `announce` delivery mode, improving user experience.
- Enhanced scheduling options to accept ISO 8601 timestamps for `schedule.at`, while still supporting epoch milliseconds.
- Refined documentation to clarify delivery modes and scheduling formats.
- Adjusted related CLI commands and UI components to reflect these changes, ensuring consistency across the platform.
- Improved handling of legacy delivery fields for backward compatibility.

This update streamlines the configuration of isolated jobs, making it easier for users to manage job outputs and schedules.
This commit is contained in:
Tyler Yust
2026-02-03 14:21:38 -08:00
committed by Peter Steinberger
parent 511c656cbc
commit 0bb0dfc9bc
13 changed files with 202 additions and 62 deletions

View File

@@ -134,4 +134,50 @@ describe("normalizeCronJobCreate", () => {
expect(delivery.channel).toBe("telegram");
expect(delivery.to).toBe("7200373102");
});
it("defaults isolated agentTurn delivery to announce", () => {
const normalized = normalizeCronJobCreate({
name: "default-announce",
enabled: true,
schedule: { kind: "cron", expr: "* * * * *" },
payload: {
kind: "agentTurn",
message: "hi",
},
}) as unknown as Record<string, unknown>;
const delivery = normalized.delivery as Record<string, unknown>;
expect(delivery.mode).toBe("announce");
});
it("does not override explicit legacy delivery fields", () => {
const normalized = normalizeCronJobCreate({
name: "legacy deliver",
enabled: true,
schedule: { kind: "cron", expr: "* * * * *" },
payload: {
kind: "agentTurn",
message: "hi",
deliver: true,
to: "7200373102",
},
}) as unknown as Record<string, unknown>;
expect(normalized.delivery).toBeUndefined();
});
it("does not override legacy isolation settings", () => {
const normalized = normalizeCronJobCreate({
name: "legacy isolation",
enabled: true,
schedule: { kind: "cron", expr: "* * * * *" },
payload: {
kind: "agentTurn",
message: "hi",
},
isolation: { postToMainPrefix: "Cron" },
}) as unknown as Record<string, unknown>;
expect(normalized.delivery).toBeUndefined();
});
});

View File

@@ -85,6 +85,19 @@ function coerceDelivery(delivery: UnknownRecord) {
return next;
}
function hasLegacyDeliveryHints(payload: UnknownRecord) {
if (typeof payload.deliver === "boolean") {
return true;
}
if (typeof payload.bestEffortDeliver === "boolean") {
return true;
}
if (typeof payload.to === "string" && payload.to.trim()) {
return true;
}
return false;
}
function unwrapJob(raw: UnknownRecord) {
if (isRecord(raw.data)) {
return raw.data;
@@ -159,6 +172,21 @@ export function normalizeCronJobInput(
next.sessionTarget = "isolated";
}
}
const hasDelivery = "delivery" in next && next.delivery !== undefined;
const payload = isRecord(next.payload) ? next.payload : null;
const payloadKind = payload && typeof payload.kind === "string" ? payload.kind : "";
const sessionTarget = typeof next.sessionTarget === "string" ? next.sessionTarget : "";
const hasLegacyIsolation = isRecord(next.isolation);
const hasLegacyDelivery = payload ? hasLegacyDeliveryHints(payload) : false;
if (
!hasDelivery &&
!hasLegacyIsolation &&
!hasLegacyDelivery &&
sessionTarget === "isolated" &&
payloadKind === "agentTurn"
) {
next.delivery = { mode: "announce" };
}
}
return next;