feat(cron): add --account flag for multi-account delivery routing (#26284)

* feat(cron): add --account flag for multi-account delivery routing

Add support for explicit delivery account routing in cron jobs across CLI, normalization, delivery planning, and isolated delivery target resolution.

Highlights:
- Add --account <id> to cron add and cron edit
- Add optional delivery.accountId to cron types and delivery plan
- Normalize and trim delivery.accountId in cron create/update normalization
- Prefer explicit accountId over session lastAccountId and bindings fallback
- Thread accountId through isolated cron run delivery resolution
- Preserve cron edit --best-effort-deliver/--no-best-effort-deliver behavior by keeping implicit announce mode
- Expand tests for account passthrough/merge/precedence and CLI account flows

* cron: resolve rebase duplicate accountId fields

---------

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
This commit is contained in:
Marvin
2026-02-28 17:57:49 +01:00
committed by GitHub
parent e1c8094ad0
commit 5e2ef0e883
13 changed files with 218 additions and 26 deletions

View File

@@ -92,6 +92,7 @@ export function registerCronAddCommand(cron: Command) {
"--to <dest>",
"Delivery destination (E.164, Telegram chatId, or Discord channel/user)",
)
.option("--account <id>", "Channel account id for delivery (multi-account setups)")
.option("--best-effort-deliver", "Do not fail the job if delivery fails", false)
.option("--json", "Output JSON", false)
.action(async (opts: GatewayRpcOpts & Record<string, unknown>, cmd?: Command) => {
@@ -221,6 +222,15 @@ export function registerCronAddCommand(cron: Command) {
throw new Error("--announce/--no-deliver require --session isolated.");
}
const accountId =
typeof opts.account === "string" && opts.account.trim()
? opts.account.trim()
: undefined;
if (accountId && (sessionTarget !== "isolated" || payload.kind !== "agentTurn")) {
throw new Error("--account requires an isolated agentTurn job with delivery.");
}
const deliveryMode =
sessionTarget === "isolated" && payload.kind === "agentTurn"
? hasAnnounce
@@ -265,6 +275,7 @@ export function registerCronAddCommand(cron: Command) {
? opts.channel.trim()
: undefined,
to: typeof opts.to === "string" && opts.to.trim() ? opts.to.trim() : undefined,
accountId,
bestEffort: opts.bestEffortDeliver ? true : undefined,
}
: undefined,

View File

@@ -59,6 +59,7 @@ export function registerCronEditCommand(cron: Command) {
"--to <dest>",
"Delivery destination (E.164, Telegram chatId, or Discord channel/user)",
)
.option("--account <id>", "Channel account id for delivery (multi-account setups)")
.option("--best-effort-deliver", "Do not fail job if delivery fails")
.option("--no-best-effort-deliver", "Fail job when delivery fails")
.action(async (id, opts) => {
@@ -209,6 +210,7 @@ export function registerCronEditCommand(cron: Command) {
const hasTimeoutSeconds = Boolean(timeoutSeconds && Number.isFinite(timeoutSeconds));
const hasDeliveryModeFlag = opts.announce || typeof opts.deliver === "boolean";
const hasDeliveryTarget = typeof opts.channel === "string" || typeof opts.to === "string";
const hasDeliveryAccount = typeof opts.account === "string";
const hasBestEffort = typeof opts.bestEffortDeliver === "boolean";
const hasAgentTurnPatch =
typeof opts.message === "string" ||
@@ -217,6 +219,7 @@ export function registerCronEditCommand(cron: Command) {
hasTimeoutSeconds ||
hasDeliveryModeFlag ||
hasDeliveryTarget ||
hasDeliveryAccount ||
hasBestEffort;
if (hasSystemEventPatch && hasAgentTurnPatch) {
throw new Error("Choose at most one payload change");
@@ -235,14 +238,14 @@ export function registerCronEditCommand(cron: Command) {
patch.payload = payload;
}
if (hasDeliveryModeFlag || hasDeliveryTarget || hasBestEffort) {
const deliveryMode =
opts.announce || opts.deliver === true
? "announce"
: opts.deliver === false
? "none"
: "announce";
const delivery: Record<string, unknown> = { mode: deliveryMode };
if (hasDeliveryModeFlag || hasDeliveryTarget || hasDeliveryAccount || hasBestEffort) {
const delivery: Record<string, unknown> = {};
if (hasDeliveryModeFlag) {
delivery.mode = opts.announce || opts.deliver === true ? "announce" : "none";
} else if (hasBestEffort) {
// Back-compat: toggling best-effort alone has historically implied announce mode.
delivery.mode = "announce";
}
if (typeof opts.channel === "string") {
const channel = opts.channel.trim();
delivery.channel = channel ? channel : undefined;
@@ -251,6 +254,10 @@ export function registerCronEditCommand(cron: Command) {
const to = opts.to.trim();
delivery.to = to ? to : undefined;
}
if (typeof opts.account === "string") {
const account = opts.account.trim();
delivery.accountId = account ? account : undefined;
}
if (typeof opts.bestEffortDeliver === "boolean") {
delivery.bestEffort = opts.bestEffortDeliver;
}