mirror of
https://github.com/openclaw/openclaw.git
synced 2026-02-08 21:09:23 +08:00
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:
committed by
Peter Steinberger
parent
511c656cbc
commit
0bb0dfc9bc
@@ -46,6 +46,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Feishu: add Feishu/Lark plugin support + docs. (#7313) Thanks @jiulingyun (openclaw-cn).
|
||||
- Web UI: add Agents dashboard for managing agent files, tools, skills, models, channels, and cron jobs.
|
||||
- Cron: add announce delivery mode for isolated jobs (CLI + Control UI) and delivery mode config.
|
||||
- Cron: default isolated jobs to announce delivery; accept ISO 8601 `schedule.at` in tool inputs.
|
||||
- Memory: implement the opt-in QMD backend for workspace memory. (#3160) Thanks @vignesh07.
|
||||
- Security: add healthcheck skill and bootstrap audit guidance. (#7641) Thanks @Takhoffman.
|
||||
- Config: allow setting a default subagent thinking level via `agents.defaults.subagents.thinking` (and per-agent `agents.list[].subagents.thinking`). (#7372) Thanks @tyler6204.
|
||||
|
||||
@@ -23,7 +23,7 @@ cron is the mechanism.
|
||||
- Jobs persist under `~/.openclaw/cron/` so restarts don’t lose schedules.
|
||||
- Two execution styles:
|
||||
- **Main session**: enqueue a system event, then run on the next heartbeat.
|
||||
- **Isolated**: run a dedicated agent turn in `cron:<jobId>`, with a delivery mode (legacy summary, announce, full output, or none).
|
||||
- **Isolated**: run a dedicated agent turn in `cron:<jobId>`, with delivery (announce by default, full output or none; legacy main summary still supported).
|
||||
- Wakeups are first-class: a job can request “wake now” vs “next heartbeat”.
|
||||
|
||||
## Quick start (actionable)
|
||||
@@ -108,7 +108,7 @@ Jobs can optionally auto-delete after a successful one-shot run via `deleteAfter
|
||||
|
||||
Cron supports three schedule kinds:
|
||||
|
||||
- `at`: one-shot timestamp (ms since epoch). Gateway accepts ISO 8601 and coerces to UTC.
|
||||
- `at`: one-shot timestamp. Prefer ISO 8601 via `schedule.at`; `atMs` (epoch ms) is also accepted.
|
||||
- `every`: fixed interval (ms).
|
||||
- `cron`: 5-field cron expression with optional IANA timezone.
|
||||
|
||||
@@ -136,12 +136,13 @@ Key behaviors:
|
||||
|
||||
- Prompt is prefixed with `[cron:<jobId> <job name>]` for traceability.
|
||||
- Each run starts a **fresh session id** (no prior conversation carry-over).
|
||||
- Legacy behavior (no `delivery` field): a summary is posted to the main session (prefix `Cron`, configurable).
|
||||
- Default behavior: if `delivery` is omitted, isolated jobs announce a summary immediately (`delivery.mode = "announce"`), unless legacy isolation settings or legacy payload delivery fields are provided.
|
||||
- Legacy behavior: jobs with legacy isolation settings, legacy payload delivery fields, or older stored jobs without `delivery` post a summary to the main session (prefix `Cron`, configurable).
|
||||
- `delivery.mode` (isolated-only) chooses what happens instead of the legacy summary:
|
||||
- `announce`: subagent-style summary delivered immediately to a chat.
|
||||
- `deliver`: full agent output delivered immediately to a chat.
|
||||
- `none`: internal only (no main summary, no delivery).
|
||||
- `wakeMode: "now"` triggers an immediate heartbeat after posting the **legacy** summary.
|
||||
- `wakeMode: "now"` only triggers an immediate heartbeat when using the legacy main-summary path.
|
||||
|
||||
Use isolated jobs for noisy, frequent, or "background chores" that shouldn't spam
|
||||
your main chat history.
|
||||
@@ -166,6 +167,9 @@ Delivery config (isolated jobs only):
|
||||
- `delivery.to`: channel-specific target (phone/chat/channel id).
|
||||
- `delivery.bestEffort`: avoid failing the job if delivery fails (deliver mode).
|
||||
|
||||
If `delivery` is omitted for isolated jobs, OpenClaw defaults to `announce` unless legacy isolation
|
||||
settings are present.
|
||||
|
||||
Legacy delivery fields (still accepted when `delivery` is omitted):
|
||||
|
||||
- `payload.deliver`: `true` to send output to a channel target.
|
||||
@@ -179,7 +183,7 @@ Isolation options (only for `session=isolated`):
|
||||
- `postToMainMode`: `summary` (default) or `full`.
|
||||
- `postToMainMaxChars`: max chars when `postToMainMode=full` (default 8000).
|
||||
|
||||
Note: isolation post-to-main settings apply to legacy jobs (no `delivery` field). If `delivery` is set, the legacy summary is skipped.
|
||||
Note: setting isolation post-to-main options opts into the legacy main-summary path (no `delivery` field). If `delivery` is set, the legacy summary is skipped.
|
||||
|
||||
### Model and thinking overrides
|
||||
|
||||
@@ -211,7 +215,7 @@ Delivery config is only valid for isolated jobs (`sessionTarget: "isolated"`).
|
||||
If `delivery.channel` or `delivery.to` is omitted, cron can fall back to the main session’s
|
||||
“last route” (the last place the agent replied).
|
||||
|
||||
Legacy behavior (no `delivery` field):
|
||||
Legacy behavior (no `delivery` field with legacy isolation settings or older jobs):
|
||||
|
||||
- If `payload.to` is set, cron auto-delivers the agent’s final output even if `payload.deliver` is omitted.
|
||||
- Use `payload.deliver: true` when you want last-route delivery without an explicit `to`.
|
||||
@@ -240,8 +244,8 @@ Prefixed targets like `telegram:...` / `telegram:group:...` are also accepted:
|
||||
## JSON schema for tool calls
|
||||
|
||||
Use these shapes when calling Gateway `cron.*` tools directly (agent tool calls or RPC).
|
||||
CLI flags accept human durations like `20m`, but tool calls use epoch milliseconds for
|
||||
`atMs` and `everyMs` (ISO timestamps are accepted for `at` times).
|
||||
CLI flags accept human durations like `20m`, but tool calls should use an ISO 8601 string
|
||||
for `schedule.at` (preferred) or epoch milliseconds for `atMs` and `everyMs`.
|
||||
|
||||
### cron.add params
|
||||
|
||||
@@ -250,7 +254,7 @@ One-shot, main session job (system event):
|
||||
```json
|
||||
{
|
||||
"name": "Reminder",
|
||||
"schedule": { "kind": "at", "atMs": 1738262400000 },
|
||||
"schedule": { "kind": "at", "at": "2026-02-01T16:00:00Z" },
|
||||
"sessionTarget": "main",
|
||||
"wakeMode": "now",
|
||||
"payload": { "kind": "systemEvent", "text": "Reminder text" },
|
||||
@@ -281,7 +285,8 @@ Recurring, isolated job with delivery:
|
||||
|
||||
Notes:
|
||||
|
||||
- `schedule.kind`: `at` (`atMs`), `every` (`everyMs`), or `cron` (`expr`, optional `tz`).
|
||||
- `schedule.kind`: `at` (`at` or `atMs`), `every` (`everyMs`), or `cron` (`expr`, optional `tz`).
|
||||
- `schedule.at` accepts ISO 8601 (timezone optional; treated as UTC when omitted).
|
||||
- `atMs` and `everyMs` are epoch milliseconds.
|
||||
- `sessionTarget` must be `"main"` or `"isolated"` and must match `payload.kind`.
|
||||
- Optional fields: `agentId`, `description`, `enabled`, `deleteAfterRun`, `delivery`, `isolation`.
|
||||
|
||||
@@ -90,7 +90,7 @@ Cron jobs run at **exact times** and can run in isolated sessions without affect
|
||||
- **Exact timing**: 5-field cron expressions with timezone support.
|
||||
- **Session isolation**: Runs in `cron:<jobId>` without polluting main history.
|
||||
- **Model overrides**: Use a cheaper or more powerful model per job.
|
||||
- **Delivery control**: Choose `announce` (summary), `deliver` (full output), or `none`. Legacy jobs still post a summary to main by default.
|
||||
- **Delivery control**: Isolated jobs default to `announce` (summary); choose `deliver` (full output) or `none` as needed. Legacy jobs still post a summary to main.
|
||||
- **Immediate delivery**: Announce/deliver modes post directly without waiting for heartbeat.
|
||||
- **No agent context needed**: Runs even if main session is idle or compacted.
|
||||
- **One-shot support**: `--at` for precise future timestamps.
|
||||
@@ -215,13 +215,13 @@ See [Lobster](/tools/lobster) for full usage and examples.
|
||||
|
||||
Both heartbeat and cron can interact with the main session, but differently:
|
||||
|
||||
| | Heartbeat | Cron (main) | Cron (isolated) |
|
||||
| ------- | ------------------------------- | ------------------------ | ---------------------- |
|
||||
| Session | Main | Main (via system event) | `cron:<jobId>` |
|
||||
| History | Shared | Shared | Fresh each run |
|
||||
| Context | Full | Full | None (starts clean) |
|
||||
| Model | Main session model | Main session model | Can override |
|
||||
| Output | Delivered if not `HEARTBEAT_OK` | Heartbeat prompt + event | Summary posted to main |
|
||||
| | Heartbeat | Cron (main) | Cron (isolated) |
|
||||
| ------- | ------------------------------- | ------------------------ | -------------------------- |
|
||||
| Session | Main | Main (via system event) | `cron:<jobId>` |
|
||||
| History | Shared | Shared | Fresh each run |
|
||||
| Context | Full | Full | None (starts clean) |
|
||||
| Model | Main session model | Main session model | Can override |
|
||||
| Output | Delivered if not `HEARTBEAT_OK` | Heartbeat prompt + event | Announce summary (default) |
|
||||
|
||||
### When to use main session cron
|
||||
|
||||
|
||||
@@ -16,6 +16,10 @@ Related:
|
||||
|
||||
Tip: run `openclaw cron --help` for the full command surface.
|
||||
|
||||
Note: isolated `cron add` jobs default to `--announce` delivery. Use `--deliver` for full output
|
||||
or `--no-deliver` to keep output internal. To opt into the legacy main-summary path, pass
|
||||
`--post-prefix` (or other `--post-*` options) without delivery flags.
|
||||
|
||||
## Common edits
|
||||
|
||||
Update delivery settings without changing the message:
|
||||
|
||||
@@ -81,7 +81,7 @@ you revoke it with `openclaw devices revoke --device <id> --role <role>`. See
|
||||
|
||||
Cron jobs panel notes:
|
||||
|
||||
- For isolated jobs, choose a delivery mode: legacy main summary, announce summary, deliver full output, or none.
|
||||
- For isolated jobs, delivery defaults to announce summary. You can switch to legacy main summary, deliver full output, or none.
|
||||
- Channel/target fields appear when announce or deliver is selected.
|
||||
|
||||
## Chat behavior
|
||||
|
||||
@@ -181,12 +181,15 @@ JOB SCHEMA (for add action):
|
||||
|
||||
SCHEDULE TYPES (schedule.kind):
|
||||
- "at": One-shot at absolute time
|
||||
{ "kind": "at", "atMs": <unix-ms-timestamp> }
|
||||
{ "kind": "at", "at": "<ISO-8601 timestamp>" } // preferred
|
||||
{ "kind": "at", "atMs": <unix-ms-timestamp> } // also accepted
|
||||
- "every": Recurring interval
|
||||
{ "kind": "every", "everyMs": <interval-ms>, "anchorMs": <optional-start-ms> }
|
||||
- "cron": Cron expression
|
||||
{ "kind": "cron", "expr": "<cron-expression>", "tz": "<optional-timezone>" }
|
||||
|
||||
ISO timestamps without an explicit timezone are treated as UTC.
|
||||
|
||||
PAYLOAD TYPES (payload.kind):
|
||||
- "systemEvent": Injects text as system event into session
|
||||
{ "kind": "systemEvent", "text": "<message>" }
|
||||
@@ -195,6 +198,7 @@ PAYLOAD TYPES (payload.kind):
|
||||
|
||||
DELIVERY (isolated-only, top-level):
|
||||
{ "mode": "none|announce|deliver", "channel": "<optional>", "to": "<optional>", "bestEffort": <optional-bool> }
|
||||
- Default for isolated agentTurn jobs (when delivery omitted): "announce"
|
||||
|
||||
LEGACY DELIVERY (payload, only when delivery is omitted):
|
||||
{ "deliver": <optional-bool>, "channel": "<optional>", "to": "<optional>", "bestEffortDeliver": <optional-bool> }
|
||||
|
||||
@@ -65,6 +65,36 @@ describe("cron cli", () => {
|
||||
expect(params?.payload?.thinking).toBe("low");
|
||||
});
|
||||
|
||||
it("defaults isolated cron add to announce delivery", async () => {
|
||||
callGatewayFromCli.mockClear();
|
||||
|
||||
const { registerCronCli } = await import("./cron-cli.js");
|
||||
const program = new Command();
|
||||
program.exitOverride();
|
||||
registerCronCli(program);
|
||||
|
||||
await program.parseAsync(
|
||||
[
|
||||
"cron",
|
||||
"add",
|
||||
"--name",
|
||||
"Daily",
|
||||
"--cron",
|
||||
"* * * * *",
|
||||
"--session",
|
||||
"isolated",
|
||||
"--message",
|
||||
"hello",
|
||||
],
|
||||
{ from: "user" },
|
||||
);
|
||||
|
||||
const addCall = callGatewayFromCli.mock.calls.find((call) => call[0] === "cron.add");
|
||||
const params = addCall?.[2] as { delivery?: { mode?: string } };
|
||||
|
||||
expect(params?.delivery?.mode).toBe("announce");
|
||||
});
|
||||
|
||||
it("sends agent id on cron add", async () => {
|
||||
callGatewayFromCli.mockClear();
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@ export function registerCronAddCommand(cron: Command) {
|
||||
)
|
||||
.option("--post-max-chars <n>", "Max chars when --post-mode=full (default 8000)", "8000")
|
||||
.option("--json", "Output JSON", false)
|
||||
.action(async (opts: GatewayRpcOpts & Record<string, unknown>) => {
|
||||
.action(async (opts: GatewayRpcOpts & Record<string, unknown>, cmd?: Command) => {
|
||||
try {
|
||||
const schedule = (() => {
|
||||
const at = typeof opts.at === "string" ? opts.at : "";
|
||||
@@ -148,6 +148,14 @@ export function registerCronAddCommand(cron: Command) {
|
||||
? sanitizeAgentId(opts.agent.trim())
|
||||
: undefined;
|
||||
|
||||
const hasAnnounce = Boolean(opts.announce);
|
||||
const hasDeliver = opts.deliver === true;
|
||||
const hasNoDeliver = opts.deliver === false;
|
||||
const deliveryFlagCount = [hasAnnounce, hasDeliver, hasNoDeliver].filter(Boolean).length;
|
||||
if (deliveryFlagCount > 1) {
|
||||
throw new Error("Choose at most one of --announce, --deliver, or --no-deliver");
|
||||
}
|
||||
|
||||
const payload = (() => {
|
||||
const systemEvent = typeof opts.systemEvent === "string" ? opts.systemEvent.trim() : "";
|
||||
const message = typeof opts.message === "string" ? opts.message.trim() : "";
|
||||
@@ -159,15 +167,6 @@ export function registerCronAddCommand(cron: Command) {
|
||||
return { kind: "systemEvent" as const, text: systemEvent };
|
||||
}
|
||||
const timeoutSeconds = parsePositiveIntOrUndefined(opts.timeoutSeconds);
|
||||
const hasAnnounce = Boolean(opts.announce);
|
||||
const hasDeliver = opts.deliver === true;
|
||||
const hasNoDeliver = opts.deliver === false;
|
||||
const deliveryFlagCount = [hasAnnounce, hasDeliver, hasNoDeliver].filter(
|
||||
Boolean,
|
||||
).length;
|
||||
if (deliveryFlagCount > 1) {
|
||||
throw new Error("Choose at most one of --announce, --deliver, or --no-deliver");
|
||||
}
|
||||
return {
|
||||
kind: "agentTurn" as const,
|
||||
message,
|
||||
@@ -179,15 +178,6 @@ export function registerCronAddCommand(cron: Command) {
|
||||
: undefined,
|
||||
timeoutSeconds:
|
||||
timeoutSeconds && Number.isFinite(timeoutSeconds) ? timeoutSeconds : undefined,
|
||||
channel:
|
||||
typeof opts.channel === "string" && opts.channel.trim()
|
||||
? opts.channel.trim()
|
||||
: "last",
|
||||
to: typeof opts.to === "string" && opts.to.trim() ? opts.to.trim() : undefined,
|
||||
bestEffortDeliver:
|
||||
!hasAnnounce && !hasDeliver && !hasNoDeliver && opts.bestEffortDeliver
|
||||
? true
|
||||
: undefined,
|
||||
};
|
||||
})();
|
||||
|
||||
@@ -204,8 +194,30 @@ export function registerCronAddCommand(cron: Command) {
|
||||
throw new Error("--announce/--deliver/--no-deliver require --session isolated.");
|
||||
}
|
||||
|
||||
const optionSource =
|
||||
typeof cmd?.getOptionValueSource === "function"
|
||||
? (name: string) => cmd.getOptionValueSource(name)
|
||||
: () => undefined;
|
||||
const hasLegacyPostConfig =
|
||||
optionSource("postPrefix") === "cli" ||
|
||||
optionSource("postMode") === "cli" ||
|
||||
optionSource("postMaxChars") === "cli";
|
||||
|
||||
if (
|
||||
hasLegacyPostConfig &&
|
||||
(sessionTarget !== "isolated" || payload.kind !== "agentTurn")
|
||||
) {
|
||||
throw new Error(
|
||||
"--post-prefix/--post-mode/--post-max-chars require --session isolated.",
|
||||
);
|
||||
}
|
||||
|
||||
if (hasLegacyPostConfig && (hasAnnounce || hasDeliver || hasNoDeliver)) {
|
||||
throw new Error("Choose legacy main-summary options or a delivery mode (not both).");
|
||||
}
|
||||
|
||||
const isolation =
|
||||
sessionTarget === "isolated"
|
||||
sessionTarget === "isolated" && hasLegacyPostConfig
|
||||
? {
|
||||
postToMainPrefix:
|
||||
typeof opts.postPrefix === "string" && opts.postPrefix.trim()
|
||||
@@ -216,12 +228,25 @@ export function registerCronAddCommand(cron: Command) {
|
||||
? opts.postMode
|
||||
: undefined,
|
||||
postToMainMaxChars:
|
||||
typeof opts.postMaxChars === "string" && /^\d+$/.test(opts.postMaxChars)
|
||||
opts.postMode === "full" &&
|
||||
typeof opts.postMaxChars === "string" &&
|
||||
/^\d+$/.test(opts.postMaxChars)
|
||||
? Number.parseInt(opts.postMaxChars, 10)
|
||||
: undefined,
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const deliveryMode =
|
||||
sessionTarget === "isolated" && payload.kind === "agentTurn" && !hasLegacyPostConfig
|
||||
? hasAnnounce
|
||||
? "announce"
|
||||
: hasDeliver
|
||||
? "deliver"
|
||||
: hasNoDeliver
|
||||
? "none"
|
||||
: "announce"
|
||||
: undefined;
|
||||
|
||||
const nameRaw = typeof opts.name === "string" ? opts.name : "";
|
||||
const name = nameRaw.trim();
|
||||
if (!name) {
|
||||
@@ -243,20 +268,18 @@ export function registerCronAddCommand(cron: Command) {
|
||||
sessionTarget,
|
||||
wakeMode,
|
||||
payload,
|
||||
delivery:
|
||||
payload.kind === "agentTurn" &&
|
||||
sessionTarget === "isolated" &&
|
||||
(opts.announce || typeof opts.deliver === "boolean")
|
||||
? {
|
||||
mode: opts.announce ? "announce" : opts.deliver === true ? "deliver" : "none",
|
||||
channel:
|
||||
typeof opts.channel === "string" && opts.channel.trim()
|
||||
? opts.channel.trim()
|
||||
: "last",
|
||||
to: typeof opts.to === "string" && opts.to.trim() ? opts.to.trim() : undefined,
|
||||
bestEffort: opts.bestEffortDeliver ? true : undefined,
|
||||
}
|
||||
: undefined,
|
||||
delivery: deliveryMode
|
||||
? {
|
||||
mode: deliveryMode,
|
||||
channel:
|
||||
typeof opts.channel === "string" && opts.channel.trim()
|
||||
? opts.channel.trim()
|
||||
: undefined,
|
||||
to: typeof opts.to === "string" && opts.to.trim() ? opts.to.trim() : undefined,
|
||||
bestEffort:
|
||||
deliveryMode === "deliver" && opts.bestEffortDeliver ? true : undefined,
|
||||
}
|
||||
: undefined,
|
||||
isolation,
|
||||
};
|
||||
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -25,9 +25,9 @@ export const DEFAULT_CRON_FORM: CronFormState = {
|
||||
wakeMode: "next-heartbeat",
|
||||
payloadKind: "systemEvent",
|
||||
payloadText: "",
|
||||
deliveryMode: "legacy",
|
||||
deliveryMode: "announce",
|
||||
deliveryChannel: "last",
|
||||
deliveryTo: "",
|
||||
timeoutSeconds: "",
|
||||
postToMainPrefix: "",
|
||||
postToMainPrefix: "Cron",
|
||||
};
|
||||
|
||||
@@ -121,6 +121,7 @@ export async function addCronJob(state: CronState) {
|
||||
to: state.cronForm.deliveryTo.trim() || undefined,
|
||||
}
|
||||
: undefined;
|
||||
const legacyPrefix = state.cronForm.postToMainPrefix.trim() || "Cron";
|
||||
const agentId = state.cronForm.agentId.trim();
|
||||
const job = {
|
||||
name: state.cronForm.name.trim(),
|
||||
@@ -133,10 +134,8 @@ export async function addCronJob(state: CronState) {
|
||||
payload,
|
||||
delivery,
|
||||
isolation:
|
||||
state.cronForm.postToMainPrefix.trim() &&
|
||||
state.cronForm.sessionTarget === "isolated" &&
|
||||
state.cronForm.deliveryMode === "legacy"
|
||||
? { postToMainPrefix: state.cronForm.postToMainPrefix.trim() }
|
||||
state.cronForm.sessionTarget === "isolated" && state.cronForm.deliveryMode === "legacy"
|
||||
? { postToMainPrefix: legacyPrefix }
|
||||
: undefined,
|
||||
};
|
||||
if (!job.name) {
|
||||
|
||||
@@ -212,7 +212,7 @@ export function renderCron(props: CronProps) {
|
||||
})}
|
||||
>
|
||||
<option value="legacy">Main summary (legacy)</option>
|
||||
<option value="announce">Announce summary</option>
|
||||
<option value="announce">Announce summary (default)</option>
|
||||
<option value="deliver">Deliver full output</option>
|
||||
<option value="none">None (internal)</option>
|
||||
</select>
|
||||
|
||||
Reference in New Issue
Block a user