From cc2f8e3351165e19240407156e795caf773dcdf7 Mon Sep 17 00:00:00 2001 From: spiceoogway Date: Fri, 30 Jan 2026 02:48:16 -0500 Subject: [PATCH] fix: Make cron systemEvents trigger autonomous agent action MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #4107 Problem: - Cron jobs with systemEvent payloads enqueue events as passive text - Agent sees events but doesn't autonomously process them - Expected behavior: agent should execute complex tasks (spawn subagents, etc.) Root Cause: - systemEvents are injected as 'System: [timestamp] text' messages - Standard heartbeat prompt is passive: 'Read HEARTBEAT.md... reply HEARTBEAT_OK' - No explicit instruction to process and act on systemEvents - Exec completion events already had directive prompt, but generic events didn't Solution: - Added SYSTEM_EVENT_PROMPT constant with directive language - Modified heartbeat runner to detect generic (non-exec) systemEvents - When generic events are present, use directive prompt instead of passive one - Directive prompt explicitly instructs agent to: 1. Check HEARTBEAT.md for event-specific handling 2. Execute required actions (spawn subagent, perform task, etc.) 3. Reply HEARTBEAT_OK only if no action needed Changes: - src/infra/heartbeat-runner.ts: - Added SYSTEM_EVENT_PROMPT constant (lines 100-105) - Modified prompt selection logic to detect generic systemEvents (lines 510-523) - Updated Provider field to reflect 'system-event' context Testing: - Build passes (TypeScript compilation successful) - Pattern follows existing exec-event precedent - Backward compatible (only affects sessions with pending systemEvents) Follow-up: - Add integration test for cron → subagent spawn workflow - Update documentation on systemEvent best practices --- src/infra/heartbeat-runner.ts | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/infra/heartbeat-runner.ts b/src/infra/heartbeat-runner.ts index ac3471adf..7388df870 100644 --- a/src/infra/heartbeat-runner.ts +++ b/src/infra/heartbeat-runner.ts @@ -97,6 +97,14 @@ const EXEC_EVENT_PROMPT = "Please relay the command output to the user in a helpful way. If the command succeeded, share the relevant output. " + "If it failed, explain what went wrong."; +// This prompt is used when generic system events (e.g., from cron jobs) are pending. +// It explicitly instructs the agent to process the events rather than just acknowledging them. +const SYSTEM_EVENT_PROMPT = + "You have received one or more system events (shown in the system messages above). " + + "Read HEARTBEAT.md if it exists for instructions on how to handle specific event types. " + + "If an event requires action (e.g., spawning a subagent, performing a task), execute that action now. " + + "If no action is needed, reply HEARTBEAT_OK."; + function resolveActiveHoursTimezone(cfg: OpenClawConfig, raw?: string): string { const trimmed = raw?.trim(); if (!trimmed || trimmed === "user") { @@ -499,15 +507,26 @@ export async function runHeartbeatOnce(opts: { // If so, use a specialized prompt that instructs the model to relay the result // instead of the standard heartbeat prompt with "reply HEARTBEAT_OK". const isExecEvent = opts.reason === "exec-event"; - const pendingEvents = isExecEvent ? peekSystemEvents(sessionKey) : []; + const pendingEvents = peekSystemEvents(sessionKey); const hasExecCompletion = pendingEvents.some((evt) => evt.includes("Exec finished")); + // Check for generic (non-exec) system events that may require action (e.g., from cron jobs). + // Use a directive prompt to ensure the agent processes them rather than just acknowledging. + const hasGenericSystemEvents = pendingEvents.length > 0 && !hasExecCompletion; - const prompt = hasExecCompletion ? EXEC_EVENT_PROMPT : resolveHeartbeatPrompt(cfg, heartbeat); + const prompt = hasExecCompletion + ? EXEC_EVENT_PROMPT + : hasGenericSystemEvents + ? SYSTEM_EVENT_PROMPT + : resolveHeartbeatPrompt(cfg, heartbeat); const ctx = { Body: prompt, From: sender, To: sender, - Provider: hasExecCompletion ? "exec-event" : "heartbeat", + Provider: hasExecCompletion + ? "exec-event" + : hasGenericSystemEvents + ? "system-event" + : "heartbeat", SessionKey: sessionKey, }; if (!visibility.showAlerts && !visibility.showOk && !visibility.useIndicator) {