From 8d1e73edc7338f5e0f0269a2ff70f6c50254216d Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 13 Dec 2025 11:33:46 +0000 Subject: [PATCH] feat(cron): always post isolated summaries to main --- docs/cron.md | 6 +++--- src/cli/cron-cli.ts | 12 ++++++++++-- src/cron/isolated-agent.ts | 17 ++++++++++++++--- src/cron/service.test.ts | 1 - src/cron/service.ts | 4 ++-- 5 files changed, 29 insertions(+), 11 deletions(-) diff --git a/docs/cron.md b/docs/cron.md index dee8056fb..ba80e581a 100644 --- a/docs/cron.md +++ b/docs/cron.md @@ -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":"agentTurn","message":string,"deliver"?:boolean,"channel"?: "last"|"whatsapp"|"telegram","to"?:string,"timeoutSeconds"?:number}` - `isolation` (optional; only meaningful for isolated jobs) - - `{"postToMain": boolean, "postToMainPrefix"?: string}` + - `{"postToMain"?: boolean, "postToMainPrefix"?: string}` - `runtime` (optional) - `{"maxAttempts"?:number,"retryBackoffMs"?:number}` (best-effort retries; defaults off) - `state` (runtime-maintained) @@ -174,8 +174,8 @@ When due: - `sessionKey = cron:` - `sessionId = store[sessionKey].sessionId` (create if missing) - 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.: - - `System: Cron "" completed: <1-line summary>` +- Isolated jobs always enqueue a summary system event to the main session when they finish (derived from the last agent text output). + - Prefix defaults to `Cron`, and can be customized via `isolation.postToMainPrefix`. ### “Run in parallel to main” diff --git a/src/cli/cron-cli.ts b/src/cli/cron-cli.ts index 3d7f2bd87..373ac67eb 100644 --- a/src/cli/cron-cli.ts +++ b/src/cli/cron-cli.ts @@ -163,7 +163,11 @@ export function registerCronCli(program: Command) { "Do not fail the job if delivery fails", 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 for summary system event", @@ -343,7 +347,11 @@ export function registerCronCli(program: Command) { "Do not fail job if delivery fails", 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 for summary system event") .action(async (id, opts) => { try { diff --git a/src/cron/isolated-agent.ts b/src/cron/isolated-agent.ts index f3816df95..9c482cd26 100644 --- a/src/cron/isolated-agent.ts +++ b/src/cron/isolated-agent.ts @@ -43,8 +43,18 @@ function assertCommandReplyConfig(cfg: ClawdisConfig) { function pickSummaryFromOutput(text: string | undefined) { const clean = (text ?? "").trim(); if (!clean) return undefined; - const oneLine = clean.replace(/\s+/g, " "); - return oneLine.length > 200 ? `${oneLine.slice(0, 200)}…` : oneLine; + const limit = 2000; + 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( @@ -247,7 +257,8 @@ export async function runCronIsolatedAgentTurn(params: { const payloads = runResult.payloads ?? []; const firstText = payloads[0]?.text ?? ""; - const summary = pickSummaryFromOutput(firstText); + const summary = + pickSummaryFromPayloads(payloads) ?? pickSummaryFromOutput(firstText); if (delivery) { if (resolvedDelivery.channel === "whatsapp") { diff --git a/src/cron/service.test.ts b/src/cron/service.test.ts index ee1a79eb5..656ccf86d 100644 --- a/src/cron/service.test.ts +++ b/src/cron/service.test.ts @@ -104,7 +104,6 @@ describe("CronService", () => { sessionTarget: "isolated", wakeMode: "now", payload: { kind: "agentTurn", message: "do it", deliver: false }, - isolation: { postToMain: true, postToMainPrefix: "Cron" }, }); vi.setSystemTime(new Date("2025-12-13T00:00:01.000Z")); diff --git a/src/cron/service.ts b/src/cron/service.ts index 2b5aa1f29..828194ba9 100644 --- a/src/cron/service.ts +++ b/src/cron/service.ts @@ -402,8 +402,8 @@ export class CronService { nextRunAtMs: job.state.nextRunAtMs, }); - if (summary && job.isolation?.postToMain) { - const prefix = job.isolation.postToMainPrefix?.trim() || "Cron"; + if (summary && job.sessionTarget === "isolated") { + const prefix = job.isolation?.postToMainPrefix?.trim() || "Cron"; this.deps.enqueueSystemEvent(`${prefix}: ${summary}`); if (job.wakeMode === "now") { this.deps.requestReplyHeartbeatNow({ reason: `cron:${job.id}:post` });