



























@@ -716,8 +716,7 @@ async function resolveHeartbeatPreflight(params: {
716716reasonFlags.isExecEventReason ||
717717reasonFlags.isCronEventReason ||
718718reasonFlags.isWakeReason ||
719-hasTaggedCronEvents ||
720-dueCommitments.length > 0;
719+hasTaggedCronEvents;
721720const basePreflight = {
722721 ...reasonFlags,
723722 session,
@@ -738,7 +737,11 @@ async function resolveHeartbeatPreflight(params: {
738737try {
739738heartbeatFileContent = await fs.readFile(heartbeatFilePath, "utf-8");
740739const tasks = parseHeartbeatTasks(heartbeatFileContent);
741-if (isHeartbeatContentEffectivelyEmpty(heartbeatFileContent) && tasks.length === 0) {
740+if (
741+isHeartbeatContentEffectivelyEmpty(heartbeatFileContent) &&
742+tasks.length === 0 &&
743+dueCommitments.length === 0
744+) {
742745return {
743746 ...basePreflight,
744747skipReason: "empty-heartbeat-file",
@@ -773,6 +776,23 @@ type HeartbeatPromptResolution = {
773776hasDueCommitments: boolean;
774777};
775778779+function resolveDueHeartbeatTasks(
780+preflight: Pick<HeartbeatPreflight, "session" | "tasks">,
781+startedAt: number,
782+): HeartbeatTask[] {
783+const tasks = preflight.tasks;
784+if (!tasks || tasks.length === 0) {
785+return [];
786+}
787+return tasks.filter((task) =>
788+isTaskDue(
789+(preflight.session.entry?.heartbeatTaskState as Record<string, number>)?.[task.name],
790+task.interval,
791+startedAt,
792+),
793+);
794+}
795+776796function appendHeartbeatWorkspacePathHint(prompt: string, workspaceDir: string): string {
777797if (!/heartbeat\.md/i.test(prompt)) {
778798return prompt;
@@ -821,6 +841,7 @@ function resolveHeartbeatRunPrompt(params: {
821841canRelayToUser: boolean;
822842workspaceDir: string;
823843startedAt: number;
844+dueTasks: HeartbeatTask[];
824845heartbeatFileContent?: string;
825846}): HeartbeatPromptResolution {
826847const pendingEventEntries = params.preflight.pendingEventEntries;
@@ -844,14 +865,7 @@ function resolveHeartbeatRunPrompt(params: {
844865const hasDueCommitments = Boolean(commitmentPrompt);
845866846867if (params.preflight.tasks && params.preflight.tasks.length > 0) {
847-const tasks = params.preflight.tasks;
848-const dueTasks = tasks.filter((task) =>
849-isTaskDue(
850-(params.preflight.session.entry?.heartbeatTaskState as Record<string, number>)?.[task.name],
851-task.interval,
852-params.startedAt,
853-),
854-);
868+const dueTasks = params.dueTasks;
855869856870if (dueTasks.length > 0) {
857871const taskList = dueTasks.map((task) => `- ${task.name}: ${task.prompt}`).join("\n");
@@ -867,15 +881,12 @@ After completing all due tasks, reply HEARTBEAT_OK.`;
867881prompt += `\n\nAdditional context from HEARTBEAT.md:\n${directives}`;
868882}
869883}
870-if (commitmentPrompt) {
871-prompt += `\n\n${commitmentPrompt}`;
872-}
873884return {
874885 prompt,
875886hasExecCompletion: false,
876887hasRelayableExecCompletion: false,
877888hasCronEvents: false,
878- hasDueCommitments,
889+hasDueCommitments: false,
879890};
880891}
881892if (commitmentPrompt) {
@@ -1002,16 +1013,18 @@ export async function runHeartbeatOnce(opts: {
10021013}
1003101410041015const previousUpdatedAt = entry?.updatedAt;
1016+const dueHeartbeatTasks = resolveDueHeartbeatTasks(preflight, startedAt);
1005101710061018// When isolatedSession is enabled, create a fresh session via the same
10071019// pattern as cron sessionTarget: "isolated". This gives the heartbeat
10081020// a new session ID (empty transcript) each run, avoiding the cost of
10091021// sending the full conversation history (~100K tokens) to the LLM.
10101022// Delivery routing still uses the main session entry (lastChannel, lastTo).
10111023const useIsolatedSession = heartbeat?.isolatedSession === true;
1012-const firstDueCommitment = canHeartbeatDeliverCommitments(heartbeat)
1013- ? preflight.dueCommitments[0]
1014- : undefined;
1024+const firstDueCommitment =
1025+canHeartbeatDeliverCommitments(heartbeat) && dueHeartbeatTasks.length === 0
1026+ ? preflight.dueCommitments[0]
1027+ : undefined;
10151028const commitmentDeliveryContext = firstDueCommitment
10161029 ? {
10171030channel: firstDueCommitment.channel,
@@ -1083,6 +1096,7 @@ export async function runHeartbeatOnce(opts: {
10831096 canRelayToUser,
10841097 workspaceDir,
10851098 startedAt,
1099+dueTasks: dueHeartbeatTasks,
10861100heartbeatFileContent: preflight.heartbeatFileContent,
10871101});
10881102const dueCommitmentIds = hasDueCommitments
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。