feat(cron): always post isolated summaries to main
This commit is contained in:
@@ -76,7 +76,7 @@ Each job is a JSON object with stable keys (unknown keys ignored for forward com
|
|||||||
- `{"kind":"systemEvent","text":string}` (enqueue as `System:`)
|
- `{"kind":"systemEvent","text":string}` (enqueue as `System:`)
|
||||||
- `{"kind":"agentTurn","message":string,"deliver"?:boolean,"channel"?: "last"|"whatsapp"|"telegram","to"?:string,"timeoutSeconds"?:number}`
|
- `{"kind":"agentTurn","message":string,"deliver"?:boolean,"channel"?: "last"|"whatsapp"|"telegram","to"?:string,"timeoutSeconds"?:number}`
|
||||||
- `isolation` (optional; only meaningful for isolated jobs)
|
- `isolation` (optional; only meaningful for isolated jobs)
|
||||||
- `{"postToMain": boolean, "postToMainPrefix"?: string}`
|
- `{"postToMain"?: boolean, "postToMainPrefix"?: string}`
|
||||||
- `runtime` (optional)
|
- `runtime` (optional)
|
||||||
- `{"maxAttempts"?:number,"retryBackoffMs"?:number}` (best-effort retries; defaults off)
|
- `{"maxAttempts"?:number,"retryBackoffMs"?:number}` (best-effort retries; defaults off)
|
||||||
- `state` (runtime-maintained)
|
- `state` (runtime-maintained)
|
||||||
@@ -174,8 +174,8 @@ When due:
|
|||||||
- `sessionKey = cron:<jobId>`
|
- `sessionKey = cron:<jobId>`
|
||||||
- `sessionId = store[sessionKey].sessionId` (create if missing)
|
- `sessionId = store[sessionKey].sessionId` (create if missing)
|
||||||
- Optionally deliver output (`payload.deliver === true`) to the configured channel/to.
|
- Optionally deliver output (`payload.deliver === true`) to the configured channel/to.
|
||||||
- If `isolation.postToMain` is true, enqueue a summary system event to main, e.g.:
|
- Isolated jobs always enqueue a summary system event to the main session when they finish (derived from the last agent text output).
|
||||||
- `System: Cron "<name>" completed: <1-line summary>`
|
- Prefix defaults to `Cron`, and can be customized via `isolation.postToMainPrefix`.
|
||||||
|
|
||||||
### “Run in parallel to main”
|
### “Run in parallel to main”
|
||||||
|
|
||||||
|
|||||||
@@ -163,7 +163,11 @@ export function registerCronCli(program: Command) {
|
|||||||
"Do not fail the job if delivery fails",
|
"Do not fail the job if delivery fails",
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
.option("--post-to-main", "Post a 1-line summary to main session", false)
|
.option(
|
||||||
|
"--post-to-main",
|
||||||
|
"Deprecated: isolated jobs always post a summary to main; use --post-prefix to customize",
|
||||||
|
false,
|
||||||
|
)
|
||||||
.option(
|
.option(
|
||||||
"--post-prefix <prefix>",
|
"--post-prefix <prefix>",
|
||||||
"Prefix for summary system event",
|
"Prefix for summary system event",
|
||||||
@@ -343,7 +347,11 @@ export function registerCronCli(program: Command) {
|
|||||||
"Do not fail job if delivery fails",
|
"Do not fail job if delivery fails",
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
.option("--post-to-main", "Post a 1-line summary to main session", false)
|
.option(
|
||||||
|
"--post-to-main",
|
||||||
|
"Deprecated: isolated jobs always post a summary to main; use --post-prefix to customize",
|
||||||
|
false,
|
||||||
|
)
|
||||||
.option("--post-prefix <prefix>", "Prefix for summary system event")
|
.option("--post-prefix <prefix>", "Prefix for summary system event")
|
||||||
.action(async (id, opts) => {
|
.action(async (id, opts) => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -43,8 +43,18 @@ function assertCommandReplyConfig(cfg: ClawdisConfig) {
|
|||||||
function pickSummaryFromOutput(text: string | undefined) {
|
function pickSummaryFromOutput(text: string | undefined) {
|
||||||
const clean = (text ?? "").trim();
|
const clean = (text ?? "").trim();
|
||||||
if (!clean) return undefined;
|
if (!clean) return undefined;
|
||||||
const oneLine = clean.replace(/\s+/g, " ");
|
const limit = 2000;
|
||||||
return oneLine.length > 200 ? `${oneLine.slice(0, 200)}…` : oneLine;
|
return clean.length > limit ? `${clean.slice(0, limit)}…` : clean;
|
||||||
|
}
|
||||||
|
|
||||||
|
function pickSummaryFromPayloads(
|
||||||
|
payloads: Array<{ text?: string | undefined }>,
|
||||||
|
) {
|
||||||
|
for (let i = payloads.length - 1; i >= 0; i--) {
|
||||||
|
const summary = pickSummaryFromOutput(payloads[i]?.text);
|
||||||
|
if (summary) return summary;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveDeliveryTarget(
|
function resolveDeliveryTarget(
|
||||||
@@ -247,7 +257,8 @@ export async function runCronIsolatedAgentTurn(params: {
|
|||||||
|
|
||||||
const payloads = runResult.payloads ?? [];
|
const payloads = runResult.payloads ?? [];
|
||||||
const firstText = payloads[0]?.text ?? "";
|
const firstText = payloads[0]?.text ?? "";
|
||||||
const summary = pickSummaryFromOutput(firstText);
|
const summary =
|
||||||
|
pickSummaryFromPayloads(payloads) ?? pickSummaryFromOutput(firstText);
|
||||||
|
|
||||||
if (delivery) {
|
if (delivery) {
|
||||||
if (resolvedDelivery.channel === "whatsapp") {
|
if (resolvedDelivery.channel === "whatsapp") {
|
||||||
|
|||||||
@@ -104,7 +104,6 @@ describe("CronService", () => {
|
|||||||
sessionTarget: "isolated",
|
sessionTarget: "isolated",
|
||||||
wakeMode: "now",
|
wakeMode: "now",
|
||||||
payload: { kind: "agentTurn", message: "do it", deliver: false },
|
payload: { kind: "agentTurn", message: "do it", deliver: false },
|
||||||
isolation: { postToMain: true, postToMainPrefix: "Cron" },
|
|
||||||
});
|
});
|
||||||
|
|
||||||
vi.setSystemTime(new Date("2025-12-13T00:00:01.000Z"));
|
vi.setSystemTime(new Date("2025-12-13T00:00:01.000Z"));
|
||||||
|
|||||||
@@ -402,8 +402,8 @@ export class CronService {
|
|||||||
nextRunAtMs: job.state.nextRunAtMs,
|
nextRunAtMs: job.state.nextRunAtMs,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (summary && job.isolation?.postToMain) {
|
if (summary && job.sessionTarget === "isolated") {
|
||||||
const prefix = job.isolation.postToMainPrefix?.trim() || "Cron";
|
const prefix = job.isolation?.postToMainPrefix?.trim() || "Cron";
|
||||||
this.deps.enqueueSystemEvent(`${prefix}: ${summary}`);
|
this.deps.enqueueSystemEvent(`${prefix}: ${summary}`);
|
||||||
if (job.wakeMode === "now") {
|
if (job.wakeMode === "now") {
|
||||||
this.deps.requestReplyHeartbeatNow({ reason: `cron:${job.id}:post` });
|
this.deps.requestReplyHeartbeatNow({ reason: `cron:${job.id}:post` });
|
||||||
|
|||||||
Reference in New Issue
Block a user