How Heartbeats Re-Enter Sessions at Specific Intervals in Prime Agent

Prime Agent implements a cron-job-based heartbeat system where AgentCronJob objects schedule recurring prompts that automatically inject back into active sessions via the promptHeartbeat method, using configurable deliveryMode settings to handle busy states.

The PrimeIntellect-ai/prime-agent repository provides a robust mechanism for periodic session re-entry through scheduled heartbeats. These heartbeats ensure long-running coding tasks receive periodic check-ins by automatically injecting prompts at defined intervals. This architecture relies on three core modules—cron-jobs.ts, agent-session.ts, and messages.ts—to handle scheduling, persistence, and runtime injection.

Heartbeat Architecture and Scheduling

Heartbeats in Prime Agent are modeled as persistent cron jobs rather than simple timers. The system defines a default recurrence pattern and flexible delivery semantics to accommodate various session states.

The AgentCronJob Data Model

Each heartbeat is stored as an AgentCronJob object with a strict schema. According to packages/coding-agent/src/core/cron-jobs.ts, the default interval is defined by the constant:

DEFAULT_HEARTBEAT_SCHEDULE = "every 5m"

This schedule string follows a natural language format (e.g., "every 5m", "every 1h") that the system parses into concrete nextRunAt timestamps. Each job also carries a deliveryMode property that dictates insertion behavior when the session is occupied:

  • "steer" – Immediately interrupts the current turn to inject the heartbeat prompt
  • "follow_up" – Queues the heartbeat to execute after the current operation completes

Creating and Persisting Heartbeat Jobs

When a session initializes, the system invokes AgentCronJobStore.createHeartbeat to register the periodic check-in. This method performs several critical validations before persistence.

According to the source code in packages/coding-agent/src/core/cron-jobs.ts, the creation flow:

  1. Parses the scheduleText (e.g., "every 5m") using parseAgentCronSchedule
  2. Validates that the schedule represents a recurring pattern rather than a one-time execution
  3. Constructs the AgentCronJob with the provided prompt, schedule, and deliveryMode
  4. Persists the job to scheduled-jobs.json for durability across process restarts
// Creating a heartbeat for the current session
const hb = cronStore.createHeartbeat({
  activeSessionId: session.id,
  sessionId: session.id,
  sessionFile: session.file,
  cwd: process.cwd(),
  label: "Keep-alive",
  prompt: "Check whether the long-running task needs another step.",
  scheduleText: "every 5m",           // ← interval definition
  deliveryMode: "steer",              // ← interrupt if busy
});

The job state—including nextRunAt, runCount, and lastRunAt—is maintained in scheduled-jobs.json and evaluated on each scheduler tick.

Runtime Re-Entry and Message Injection

When the scheduler determines that nextRunAt has elapsed, the heartbeat must re-enter the session context. This occurs through the AgentSession.promptHeartbeat method implemented in packages/coding-agent/src/core/agent-session.ts.

The re-entry mechanism follows this sequence:

  1. Message Construction – createHeartbeatPromptMessage(job) (defined in packages/coding-agent/src/core/messages.ts) builds a specialized message object:
const msg = createHeartbeatPromptMessage(hb);
// → {
//     role: "custom",
//     customType: HEARTBEAT_PROMPT_CUSTOM_TYPE,
//     content: hb.prompt,
//     details: { jobId: hb.id, schedule: hb.schedule, runCount: hb.runCount },
//   }
  1. Session Injection – promptHeartbeat calls _promptInjectedMessage with specific parameters to ensure proper re-entry:
    • followUpQueueKey: Set to `heartbeat:${job.id}` to isolate heartbeat messages from user traffic
    • resumeIfIdle: Set to true so the session resumes execution even if currently idle
    • customType: Uses HEARTBEAT_PROMPT_CUSTOM_TYPE to identify the message as a system heartbeat rather than user input
// Simplified scheduler tick logic
if (new Date() >= new Date(hb.nextRunAt!)) {
  await session.promptHeartbeat(hb); // Re-enters session with heartbeat prompt
}

Handling Session State and Delivery Modes

Once injected, the heartbeat's behavior depends on the concurrency model selected at creation time. The deliveryMode determines how AgentSession processes the prompt relative to ongoing activity.

"steer" Mode (Interrupt)

  • Immediately pauses current agent reasoning
  • Injects the heartbeat prompt into the active context
  • Suitable for critical health checks or timeout prevention

"follow_up" Mode (Queue)

  • Appends the heartbeat to the session's follow-up queue
  • Executes only after the current turn completes
  • Prevents disruption of complex multi-step operations

After successful execution, the scheduler increments runCount, recalculates the next execution time via parseAgentCronSchedule, and atomically updates scheduled-jobs.json with the new nextRunAt timestamp.

Summary

  • Heartbeats use AgentCronJob objects with a default DEFAULT_HEARTBEAT_SCHEDULE of "every 5m" defined in packages/coding-agent/src/core/cron-jobs.ts
  • AgentCronJobStore.createHeartbeat parses and validates schedules, persisting job state to scheduled-jobs.json for durability
  • AgentSession.promptHeartbeat re-enters sessions by injecting messages via _promptInjectedMessage with resumeIfIdle: true and a unique followUpQueueKey
  • deliveryMode controls concurrency behavior: "steer" interrupts active turns while "follow_up" queues for later execution
  • Message typing via HEARTBEAT_PROMPT_CUSTOM_TYPE enables proper UI rendering and runtime tracking in packages/coding-agent/src/core/messages.ts

Frequently Asked Questions

What is the default heartbeat interval in Prime Agent?

The default interval is defined by the constant DEFAULT_HEARTBEAT_SCHEDULE = "every 5m" in packages/coding-agent/src/core/cron-jobs.ts. This can be overridden by passing a custom scheduleText parameter when calling createHeartbeat.

How does Prime Agent handle heartbeats when a session is busy?

The system checks the deliveryMode property of the AgentCronJob. When set to "steer", the heartbeat interrupts the current operation immediately. When set to "follow_up", the heartbeat is queued in the session's follow-up queue using the key `heartbeat:${job.id}` and executes after the current turn completes.

Can heartbeats resume an idle session?

Yes. The promptHeartbeat method explicitly passes resumeIfIdle: true to _promptInjectedMessage when injecting the heartbeat prompt. This parameter ensures that even if the session has entered an idle state, the scheduler will wake it to process the periodic check-in.

Where are heartbeat jobs stored between scheduler ticks?

All heartbeat jobs are persisted to a file named scheduled-jobs.json in the working directory. This allows the scheduler to maintain nextRunAt timestamps, runCount statistics, and delivery configurations across application restarts without losing the temporal state of periodic tasks.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →