diff --git a/fix2.py b/fix2.py new file mode 100644 index 00000000000..763c97948d0 --- /dev/null +++ b/fix2.py @@ -0,0 +1,84 @@ +with open('src/infra/heartbeat-runner.ts', 'r') as f: + content = f.read() + +# Fix 1: Add heartbeatFileContent param to resolveHeartbeatRunPrompt +old_sig = """function resolveHeartbeatRunPrompt(params: { + cfg: OpenClawConfig; + heartbeat?: HeartbeatConfig; + preflight: HeartbeatPreflight; + canRelayToUser: boolean; + workspaceDir: string; + startedAt: number; +}): HeartbeatPromptResolution {""" + +new_sig = """function resolveHeartbeatRunPrompt(params: { + cfg: OpenClawConfig; + heartbeat?: HeartbeatConfig; + preflight: HeartbeatPreflight; + canRelayToUser: boolean; + workspaceDir: string; + startedAt: number; + heartbeatFileContent?: string; +}): HeartbeatPromptResolution {""" + +content = content.replace(old_sig, new_sig) + +# Fix 2: Update the task-mode prompt to include HEARTBEAT.md directives +old_prompt = ''' if (dueTasks.length > 0) { + const taskList = dueTasks.map((task) => `- ${task.name}: ${task.prompt}`).join("\\n"); + const prompt = `Run the following periodic tasks (only those due based on their intervals): + +${taskList} + +After completing all due tasks, reply HEARTBEAT_OK.`; + return { prompt, hasExecCompletion: false, hasCronEvents: false }; + }''' + +new_prompt = ''' if (dueTasks.length > 0) { + const taskList = dueTasks.map((task) => `- ${task.name}: ${task.prompt}`).join("\\n"); + let prompt = `Run the following periodic tasks (only those due based on their intervals): + +${taskList} + +After completing all due tasks, reply HEARTBEAT_OK.`; + + // Preserve HEARTBEAT.md directives (non-task content) + if (params.heartbeatFileContent) { + const directives = params.heartbeatFileContent + .replace(/^tasks:\\n(?:[ \\t].*\\n)*/m, "") + .trim(); + if (directives) { + prompt += `\\n\\nAdditional context from HEARTBEAT.md:\\n${directives}`; + } + } + return { prompt, hasExecCompletion: false, hasCronEvents: false }; + }''' + +content = content.replace(old_prompt, new_prompt) + +# Fix 3: Pass heartbeatFileContent from call site +old_call = """ const { prompt, hasExecCompletion, hasCronEvents } = resolveHeartbeatRunPrompt({ + cfg, + heartbeat, + preflight, + canRelayToUser, + workspaceDir, + startedAt, + });""" + +new_call = """ const { prompt, hasExecCompletion, hasCronEvents } = resolveHeartbeatRunPrompt({ + cfg, + heartbeat, + preflight, + canRelayToUser, + workspaceDir, + startedAt, + heartbeatFileContent: preflight.heartbeatFileContent, + });""" + +content = content.replace(old_call, new_call) + +with open('src/infra/heartbeat-runner.ts', 'w') as f: + f.write(content) + +print("Fix #2 applied: HEARTBEAT.md directives preserved in task-mode prompt") diff --git a/src/infra/heartbeat-runner.ts b/src/infra/heartbeat-runner.ts index 140a09a2569..8cc115bc123 100644 --- a/src/infra/heartbeat-runner.ts +++ b/src/infra/heartbeat-runner.ts @@ -520,6 +520,7 @@ function resolveHeartbeatRunPrompt(params: { canRelayToUser: boolean; workspaceDir: string; startedAt: number; + heartbeatFileContent?: string; }): HeartbeatPromptResolution { const pendingEventEntries = params.preflight.pendingEventEntries; const pendingEvents = params.preflight.shouldInspectPendingEvents @@ -548,11 +549,21 @@ function resolveHeartbeatRunPrompt(params: { if (dueTasks.length > 0) { const taskList = dueTasks.map((task) => `- ${task.name}: ${task.prompt}`).join("\n"); - const prompt = `Run the following periodic tasks (only those due based on their intervals): + let prompt = `Run the following periodic tasks (only those due based on their intervals): ${taskList} After completing all due tasks, reply HEARTBEAT_OK.`; + + // Preserve HEARTBEAT.md directives (non-task content) + if (params.heartbeatFileContent) { + const directives = params.heartbeatFileContent + .replace(/^tasks:\n(?:[ \t].*\n)*/m, "") + .trim(); + if (directives) { + prompt += `\n\nAdditional context from HEARTBEAT.md:\n${directives}`; + } + } return { prompt, hasExecCompletion: false, hasCronEvents: false }; } // No tasks due - skip this heartbeat to avoid wasteful API calls @@ -696,6 +707,7 @@ export async function runHeartbeatOnce(opts: { canRelayToUser, workspaceDir, startedAt, + heartbeatFileContent: preflight.heartbeatFileContent, }); // If no tasks are due, skip heartbeat entirely