mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 17:34:35 +00:00
fix(heartbeat): run when HEARTBEAT.md is missing
This commit is contained in:
@@ -41,7 +41,7 @@ import { CommandLane } from "../process/lanes.js";
|
||||
import { normalizeAgentId, toAgentStoreSessionKey } from "../routing/session-key.js";
|
||||
import { defaultRuntime, type RuntimeEnv } from "../runtime.js";
|
||||
import { escapeRegExp } from "../utils.js";
|
||||
import { formatErrorMessage } from "./errors.js";
|
||||
import { formatErrorMessage, hasErrnoCode } from "./errors.js";
|
||||
import { isWithinActiveHours } from "./heartbeat-active-hours.js";
|
||||
import {
|
||||
buildCronEventPrompt,
|
||||
@@ -481,7 +481,7 @@ type HeartbeatReasonFlags = {
|
||||
isWakeReason: boolean;
|
||||
};
|
||||
|
||||
type HeartbeatSkipReason = "empty-heartbeat-file" | "no-heartbeat-file";
|
||||
type HeartbeatSkipReason = "empty-heartbeat-file";
|
||||
|
||||
type HeartbeatPreflight = HeartbeatReasonFlags & {
|
||||
session: ReturnType<typeof resolveHeartbeatSession>;
|
||||
@@ -525,42 +525,39 @@ async function resolveHeartbeatPreflight(params: {
|
||||
reasonFlags.isCronEventReason ||
|
||||
reasonFlags.isWakeReason ||
|
||||
hasTaggedCronEvents;
|
||||
|
||||
const workspaceDir = resolveAgentWorkspaceDir(params.cfg, params.agentId);
|
||||
const heartbeatFilePath = path.join(workspaceDir, DEFAULT_HEARTBEAT_FILENAME);
|
||||
try {
|
||||
const heartbeatFileContent = await fs.readFile(heartbeatFilePath, "utf-8");
|
||||
if (isHeartbeatContentEffectivelyEmpty(heartbeatFileContent) && !shouldBypassFileGates) {
|
||||
return {
|
||||
...reasonFlags,
|
||||
session,
|
||||
pendingEventEntries,
|
||||
hasTaggedCronEvents,
|
||||
shouldInspectPendingEvents,
|
||||
skipReason: "empty-heartbeat-file",
|
||||
};
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
if ((err as NodeJS.ErrnoException)?.code === "ENOENT" && !shouldBypassFileGates) {
|
||||
return {
|
||||
...reasonFlags,
|
||||
session,
|
||||
pendingEventEntries,
|
||||
hasTaggedCronEvents,
|
||||
shouldInspectPendingEvents,
|
||||
skipReason: "no-heartbeat-file",
|
||||
};
|
||||
}
|
||||
// For other read errors, proceed with heartbeat as before.
|
||||
}
|
||||
|
||||
return {
|
||||
const basePreflight = {
|
||||
...reasonFlags,
|
||||
session,
|
||||
pendingEventEntries,
|
||||
hasTaggedCronEvents,
|
||||
shouldInspectPendingEvents,
|
||||
};
|
||||
} satisfies Omit<HeartbeatPreflight, "skipReason">;
|
||||
|
||||
if (shouldBypassFileGates) {
|
||||
return basePreflight;
|
||||
}
|
||||
|
||||
const workspaceDir = resolveAgentWorkspaceDir(params.cfg, params.agentId);
|
||||
const heartbeatFilePath = path.join(workspaceDir, DEFAULT_HEARTBEAT_FILENAME);
|
||||
try {
|
||||
const heartbeatFileContent = await fs.readFile(heartbeatFilePath, "utf-8");
|
||||
if (isHeartbeatContentEffectivelyEmpty(heartbeatFileContent)) {
|
||||
return {
|
||||
...basePreflight,
|
||||
skipReason: "empty-heartbeat-file",
|
||||
};
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
if (hasErrnoCode(err, "ENOENT")) {
|
||||
// Missing HEARTBEAT.md is intentional in some setups (for example, when
|
||||
// heartbeat instructions live outside the file), so keep the run active.
|
||||
// The heartbeat prompt already says "if it exists".
|
||||
return basePreflight;
|
||||
}
|
||||
// For other read errors, proceed with heartbeat as before.
|
||||
}
|
||||
|
||||
return basePreflight;
|
||||
}
|
||||
|
||||
export async function runHeartbeatOnce(opts: {
|
||||
|
||||
Reference in New Issue
Block a user