diff --git a/src/agents/subagent-announce.ts b/src/agents/subagent-announce.ts index 32cf49cc2db..6861ce85a11 100644 --- a/src/agents/subagent-announce.ts +++ b/src/agents/subagent-announce.ts @@ -70,12 +70,17 @@ function buildCompletionDeliveryMessage(params: { subagentName: string; spawnMode?: SpawnSubagentMode; outcome?: SubagentRunOutcome; + announceType?: SubagentAnnounceType; }): string { const findingsText = params.findings.trim(); if (isAnnounceSkip(findingsText)) { return ""; } const hasFindings = findingsText.length > 0 && findingsText !== "(no output)"; + // Cron completions are standalone messages — skip the subagent status header. + if (params.announceType === "cron job") { + return hasFindings ? findingsText : ""; + } const header = (() => { if (params.outcome?.status === "error") { return params.spawnMode === "session" @@ -1278,6 +1283,7 @@ export async function runSubagentAnnounceFlow(params: { subagentName, spawnMode: params.spawnMode, outcome, + announceType, }); const internalSummaryMessage = [ `[System Message] [sessionId: ${announceSessionId}] A ${announceType} "${taskLabel}" just ${statusLabel}.`, diff --git a/src/cron/isolated-agent.direct-delivery-forum-topics.test.ts b/src/cron/isolated-agent.direct-delivery-forum-topics.test.ts index 176a8cd5a66..6beaac8164a 100644 --- a/src/cron/isolated-agent.direct-delivery-forum-topics.test.ts +++ b/src/cron/isolated-agent.direct-delivery-forum-topics.test.ts @@ -1,5 +1,5 @@ import "./isolated-agent.mocks.js"; -import { beforeEach, describe, expect, it } from "vitest"; +import { beforeEach, describe, expect, it, vi } from "vitest"; import { runSubagentAnnounceFlow } from "../agents/subagent-announce.js"; import { createCliDeps, @@ -56,6 +56,10 @@ describe("runCronIsolatedAgentTurn forum topic delivery", () => { expect(res.status).toBe("ok"); expect(runSubagentAnnounceFlow).toHaveBeenCalledTimes(1); + const announceArgs = vi.mocked(runSubagentAnnounceFlow).mock.calls[0]?.[0] as + | { expectsCompletionMessage?: boolean } + | undefined; + expect(announceArgs?.expectsCompletionMessage).toBe(true); expect(deps.sendMessageTelegram).not.toHaveBeenCalled(); }); }); diff --git a/src/cron/isolated-agent.skips-delivery-without-whatsapp-recipient-besteffortdeliver-true.test.ts b/src/cron/isolated-agent.skips-delivery-without-whatsapp-recipient-besteffortdeliver-true.test.ts index 01a407692e0..0665be347f0 100644 --- a/src/cron/isolated-agent.skips-delivery-without-whatsapp-recipient-besteffortdeliver-true.test.ts +++ b/src/cron/isolated-agent.skips-delivery-without-whatsapp-recipient-besteffortdeliver-true.test.ts @@ -89,6 +89,9 @@ async function expectExplicitTelegramTargetAnnounce(params: { expect(announceArgs?.requesterOrigin?.to).toBe("123"); expect(announceArgs?.roundOneReply).toBe(params.expectedText); expect(announceArgs?.bestEffortDeliver).toBe(false); + expect((announceArgs as { expectsCompletionMessage?: boolean })?.expectsCompletionMessage).toBe( + true, + ); expect(deps.sendMessageTelegram).not.toHaveBeenCalled(); }); } diff --git a/src/cron/isolated-agent/delivery-dispatch.ts b/src/cron/isolated-agent/delivery-dispatch.ts index b071f63172d..2c6748a99ae 100644 --- a/src/cron/isolated-agent/delivery-dispatch.ts +++ b/src/cron/isolated-agent/delivery-dispatch.ts @@ -309,6 +309,10 @@ export async function dispatchCronDelivery( timeoutMs: params.timeoutMs, cleanup: params.job.deleteAfterRun ? "delete" : "keep", roundOneReply: synthesizedText, + // Cron output is a finished completion message: send it directly to the + // target channel via the completion-direct-send path rather than injecting + // a trigger message into the (likely idle) main agent session. + expectsCompletionMessage: true, // Keep delivery outcome truthful for cron state: if outbound send fails, // announce flow must report false so caller can apply best-effort policy. bestEffortDeliver: false,