Skip to content

Commit 72aa3f1

Browse files
LZY3538claudesteipete
authored
fix(cron): allow silent scheduled runs (#95725)
* fix(cron): pass allowEmptyAssistantReplyAsSilent from cron executor Intentionally-silent cron jobs (alert watchers that say nothing unless there is something to report) fail with FailoverError "empty_response" because the allowEmptyAssistantReplyAsSilent flag is only computed for DM/group chat contexts — the cron executor never sets it. Instead of patching the shared CLI runner with a lane substring heuristic, set allowEmptyAssistantReplyAsSilent: true at the cron executor level for both the CLI and embedded runner paths. This: - Owns the silent-empty policy at the correct boundary (the cron executor) - Covers both runner paths uniformly - Does not broaden the shared CLI empty-response guard for non-cron callers Fixes #94224 Co-Authored-By: Claude <[email protected]> * test(cron): add regression test for allowEmptyAssistantReplyAsSilent flag propagation Verify that the cron executor passes allowEmptyAssistantReplyAsSilent: true to both runCliAgent and runEmbeddedAgent calls. The embedded test uses mockTexts: [""] to truly exercise empty output, and the CLI test mocks runCliAgent with empty payload text — both scenarios require the flag to avoid empty_response errors on intentionally-silent cron watchers. Co-Authored-By: Claude <[email protected]> * refactor(cron): centralize silent reply policy Reuse existing executor suites and keep CLI and embedded cron runners aligned. Co-authored-by: 刘镇业 0668001127 <[email protected]> --------- Co-authored-by: Claude <[email protected]> Co-authored-by: Peter Steinberger <[email protected]>
1 parent ae12413 commit 72aa3f1

3 files changed

Lines changed: 7 additions & 0 deletions

File tree

src/cron/isolated-agent/run-executor.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,8 @@ export function createCronPromptExecutor(params: {
277277
resolveFallbackCronSourceDeliveryPlan(params.job, params.resolvedDelivery);
278278
const sourceReplyDeliveryMode = sourceDelivery.sourceReplyDeliveryMode;
279279
const messageChannel = sourceDelivery.target.channel ?? params.resolvedDelivery.channel;
280+
// Cron prompts may intentionally have nothing to report; both runners must agree on silence.
281+
const allowEmptyAssistantReplyAsSilent = true;
280282
const deliveryTargetRuntimeContext = buildCronDeliveryTargetRuntimeContext({
281283
resolvedDeliveryOk: params.resolvedDeliveryOk,
282284
messageToolPromptEnabled: params.messageToolPromptEnabled,
@@ -427,6 +429,7 @@ export function createCronPromptExecutor(params: {
427429
timeoutMs: params.timeoutMs,
428430
runId: params.cronSession.sessionEntry.sessionId,
429431
lane: resolveCronAgentLane(params.lane),
432+
allowEmptyAssistantReplyAsSilent,
430433
cliSessionId: cliSessionBinding?.sessionId,
431434
cliSessionBinding: guardedCliSessionBinding,
432435
skillsSnapshot: params.skillsSnapshot,
@@ -541,6 +544,7 @@ export function createCronPromptExecutor(params: {
541544
: undefined,
542545
sourceReplyDeliveryMode,
543546
runId: params.cronSession.sessionEntry.sessionId,
547+
allowEmptyAssistantReplyAsSilent,
544548
requireExplicitMessageTarget: sourceDelivery.messageTool.requireExplicitTarget,
545549
disableMessageTool: !sourceDelivery.messageTool.enabled,
546550
forceMessageTool: sourceDelivery.messageTool.force,

src/cron/isolated-agent/run.message-tool-policy.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,7 @@ describe("runCronIsolatedAgentTurn message tool policy", () => {
755755
expectRecordFields(
756756
getMockCallArg(runCliAgentMock, 0, 0, "CLI run"),
757757
{
758+
allowEmptyAssistantReplyAsSilent: true,
758759
messageChannel: "messagechat",
759760
requireExplicitMessageTarget: true,
760761
},

src/cron/isolated-agent/run.source-delivery-guard.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@ describe("createCronPromptExecutor sourceDelivery guard", () => {
288288
expect(runEmbeddedAgentMock).toHaveBeenCalledTimes(1);
289289
const args = getEmbeddedRunArg();
290290
expect(args.sourceReplyDeliveryMode).toBeUndefined();
291+
expect(args.allowEmptyAssistantReplyAsSilent).toBe(true);
291292
expect(args.requireExplicitMessageTarget).toBe(false);
292293
expect(args.disableMessageTool).toBe(false);
293294
expect(args.forceMessageTool).toBe(false);
@@ -308,6 +309,7 @@ describe("createCronPromptExecutor sourceDelivery guard", () => {
308309
expect(runEmbeddedAgentMock).toHaveBeenCalledTimes(1);
309310
const args = getEmbeddedRunArg();
310311
expect(args.sourceReplyDeliveryMode).toBeUndefined();
312+
expect(args.allowEmptyAssistantReplyAsSilent).toBe(true);
311313
expect(args.disableMessageTool).toBe(false);
312314
expect(args.forceMessageTool).toBe(false);
313315
expect(args.messageChannel).toBe("messagechat");

0 commit comments

Comments
 (0)