# How Prime Agent Schedules Tasks with Cron Jobs and Heartbeats

> Discover how Prime Agent schedules tasks with cron jobs and heartbeats. Learn about its in-process scheduler for persistence and execution, preventing session timeouts.

- Repository: [Prime Intellect/prime-agent](https://github.com/PrimeIntellect-ai/prime-agent)
- Tags: how-to-guide
- Published: 2026-08-18

---

**Prime Agent implements a lightweight, in-process scheduler using `AgentCronJobStore` for disk persistence and `AgentCronScheduler` for periodic execution to manage both user-defined cron jobs and internal heartbeat jobs that prevent session timeouts.**

The **PrimeIntellect-ai/prime-agent** repository provides a coding agent infrastructure where background task scheduling and session health monitoring are critical. The system handles both user automation through cron expressions and internal "heartbeat" mechanisms that keep agent sessions alive, all orchestrated through a unified scheduling layer in [`packages/coding-agent/src/core/cron-jobs.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/coding-agent/src/core/cron-jobs.ts).

## Core Architecture: Store and Scheduler

The scheduling subsystem centers on two complementary classes that separate persistence from execution.

### AgentCronJobStore (Persistence)

The **`AgentCronJobStore`** class handles durable storage of job definitions on disk, typically under `~/.prime-agent/cron-jobs/`. It provides atomic CRUD operations including `create()`, `createHeartbeat()`, `list()`, `cancel()`, and `updateRlmHeartbeat()`. Each job entry tracks its **`source`** (either `"cron"` or `"heartbeat"`), **`status`** (active, paused, or cancelled), and the **`nextRunAt`** timestamp that determines execution order.

### AgentCronScheduler (Execution Engine)

The **`AgentCronScheduler`** class manages an internal timer that ticks every second by default. On each tick, it calls **`runDue(date)`** to query the store for jobs whose `nextRunAt` timestamp has passed. When jobs are due, the scheduler invokes their handlers and recomputes the next execution time. The scheduler exposes **`start()`** and **`stop()`** methods to control the timing loop lifecycle.

## How Cron Jobs Work

User-defined cron jobs are stored with **`source: "cron"`** and contain a standard cron expression (e.g., `*/5 * * * *`) or a single-run `at` timestamp. The creation flow begins when a client sends a **`cron_add`** RPC to the daemon, which validates the payload and persists it via `AgentCronJobStore.create()`. When the scheduler’s tick detects the job is due, it executes the associated handler function and reschedules the job based on its expression.

Cancellation flows through the **`cron_cancel`** RPC, which updates the job’s `status` to `"cancelled"` in the store, causing the scheduler to skip it on subsequent ticks. Integration tests in [`packages/coding-agent/test/daemon-supervisor-process.test.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/coding-agent/test/daemon-supervisor-process.test.ts) validate this RPC-to-scheduler pipeline.

## Maintaining Session Liveness with Heartbeats

Heartbeat jobs are specialized cron entries created via **`AgentCronJobStore.createHeartbeat()`**, distinguished by **`source: "heartbeat"`**. These jobs periodically emit lightweight "ping" messages to the daemon to update the session’s last-seen timestamp, preventing timeout eviction.

Key heartbeat characteristics include:

- **`intervalSeconds`**: Configurable execution interval (defaults to 30 seconds)
- **`status`**: Supports `"active"`, `"paused"`, and `"cancelled"` states
- **Recovery**: Survives daemon restarts by reloading from disk, validated by [`packages/coding-agent/test/suite/regressions/4657-update-heartbeat-recovery.test.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/coding-agent/test/suite/regressions/4657-update-heartbeat-recovery.test.ts)

When paused via **`updateRlmHeartbeat(sessionId, jobId, { status: "pause" })"**, the scheduler temporarily skips the job without removing it from the store, allowing seamless resumption later.

## The Scheduling Lifecycle

The complete execution flow follows these distinct stages:

1. **Creation**: Client sends `cron_add` RPC with either a cron expression or heartbeat configuration
2. **Persistence**: Daemon writes the job to `AgentCronJobStore` on disk
3. **Ticking**: `AgentCronScheduler.start()` initiates a 1-second periodic timer
4. **Execution**: `runDue(now)` identifies due jobs and invokes their handlers—user commands for cron jobs, daemon pings for heartbeats
5. **Rescheduling**: Upon completion, the handler updates `nextRunAt` (or removes one-off jobs)

## Resilience and Edge Cases

Prime Agent’s scheduler handles several failure modes to ensure robust operation:

- **Process Isolation**: Each agent process runs its own scheduler instance; a supervisor process aggregates jobs across workers to prevent orphaned heartbeats if a worker crashes (see [`packages/coding-agent/test/suite/regressions/4527-worker-heartbeat-scheduling.test.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/coding-agent/test/suite/regressions/4527-worker-heartbeat-scheduling.test.ts))
- **Graceful Degradation**: Jobs marked `"cancelled"` are immediately dropped from the execution queue without requiring a scheduler restart
- **State Recovery**: If the daemon restarts, `AgentCronJobStore` reloads persisted jobs from disk, and `AgentCronScheduler` resumes the ticking loop without losing pending executions

## Implementation Examples

The following TypeScript patterns demonstrate how to interact with the scheduling API:

```typescript
import { AgentCronJobStore, AgentCronScheduler } from '../core/cron-jobs';

// Initialize the store and scheduler
const cronStore = new AgentCronJobStore();
const cronScheduler = new AgentCronScheduler(cronStore);

// Create a regular cron job running every minute
const regularJob = cronStore.create({
  prompt: "remind me",
  schedule: "*/1 * * * *",
  source: "cron",
  handler: async () => {
    await client.request({ type: "message", content: "⏰ One minute passed" });
  },
});

// Create a heartbeat that pings the daemon every 30 seconds
const heartbeat = cronStore.createHeartbeat({
  intervalSeconds: 30,
  source: "heartbeat",
  handler: async () => {
    await client.request({ type: "heartbeat" });
  },
});

// Start the scheduler (typically called by the daemon on startup)
cronScheduler.start();

// Pause the heartbeat without cancelling it
cronStore.updateRlmHeartbeat(sessionId, heartbeat.id, { status: "pause" });

```

## Summary

- **Prime Agent** uses `AgentCronJobStore` for durable, disk-backed job persistence and `AgentCronScheduler` for the execution loop.
- **Cron jobs** (`source: "cron"`) handle user automation via standard cron expressions managed through `cron_add` and `cron_cancel` RPCs.
- **Heartbeat jobs** (`source: "heartbeat"`) maintain session liveness through periodic pings, supporting pause/resume via `updateRlmHeartbeat()`.
- The system recovers gracefully from daemon restarts by reloading jobs from `~/.prime-agent/cron-jobs/`, as validated by regression test suites.
- Per-process isolation ensures that crashed workers do not leak scheduler resources or stale heartbeats.

## Frequently Asked Questions

### What is the default heartbeat interval in Prime Agent?

The default interval is **30 seconds**, configurable via the `intervalSeconds` parameter when calling `AgentCronJobStore.createHeartbeat()`.

### How does Prime Agent recover scheduled jobs after a daemon restart?

The daemon reloads the `AgentCronJobStore` from its disk location (`~/.prime-agent/cron-jobs/`), allowing the scheduler to resume ticking without losing pending jobs. This behavior is validated by the regression test in [`packages/coding-agent/test/suite/regressions/4657-update-heartbeat-recovery.test.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/coding-agent/test/suite/regressions/4657-update-heartbeat-recovery.test.ts).

### Can heartbeat jobs be paused without cancelling them?

Yes. Calling `updateRlmHeartbeat()` with `status: "pause"` suspends the heartbeat execution while preserving its configuration, enabling later resumption without re-creating the job.

### What distinguishes a heartbeat job from a regular cron job in the source code?

Heartbeat jobs have **`source: "heartbeat"`** versus **`source: "cron"`** for user tasks, are created via `createHeartbeat()` instead of `create()`, and support specific lifecycle states (active, paused, cancelled) tailored to session management rather than general automation.