Paperclip Heartbeat System Configuration Options: Complete Guide
Paperclip's heartbeat system is configured through a typed JSON schema with options for enabling the scheduler, setting the wake-up interval, controlling concurrent runs, and tracking runtime state across agents and the UI.
The heartbeat system in paperclipai/paperclip drives periodic "wake-up" cycles that keep agents responsive and the internal scheduler healthy. Operators control this behavior through instance-level configuration options defined in the shared type system and exposed via API and UI. This guide covers every configuration field, its purpose, and how to modify it programmatically.
Core Configuration Options
The heartbeat configuration lives on the instance level and synchronizes across the server, UI, and adapter utilities. All options are defined in packages/shared/src/types/heartbeat.ts and packages/shared/src/types/instance.ts.
Static Configuration (Editable)
These settings persist and control baseline behavior:
| Option | Type | Default | Purpose |
|---|---|---|---|
heartbeatEnabled |
boolean |
false |
Master toggle for the entire heartbeat scheduler. When false, no timer-driven runs queue. |
intervalSec |
number |
900 (15 min) |
Seconds between automatic heartbeat runs. Lower values increase agent responsiveness at higher resource cost. |
schedulerActive |
boolean |
true |
Allows the UI to pause scheduling without disabling the system entirely. Distinguishes capability from current state. |
maxConcurrentRuns |
number |
2 |
Internal ceiling on simultaneous heartbeat runs per company. Prevents cascade failures when runs stall. |
Runtime State (System-Managed)
These fields track live execution and appear in API responses:
| Option | Type | Description |
|---|---|---|
lastHeartbeatAt |
Date | null |
Timestamp of the most recent successful run. The UI displays this as a health indicator; the watchdog uses it to detect stalls. |
heartbeatRunId |
string | null |
Unique identifier attached to all logs, cost records, and task updates for attribution. |
runLivenessState |
"queued" | "running" | "completed" | "failed" |
Lifecycle state rendered as progress bars and status pills in the UI. |
heartbeatRunOutputSilence |
object |
Tracks silent periods with suspicionThresholdMs and criticalThresholdMs thresholds. Surfaces warnings when no stdout/stderr appears. |
heartbeatInvocationSource |
"timer" | "manual" | "api" |
Origin of the run: automatic timer, admin action, or external API. |
triggerDetail |
object |
WakeupTriggerDetail payload containing the triggering issue and reason, propagated through the run-graph for tracing. |
Where Configuration Lives
Paperclip's type-safe architecture ensures consistency across components:
packages/shared/src/types/heartbeat.ts— DefinesHeartbeatRun,HeartbeatRunEvent, andInstanceSchedulerHeartbeatAgentinterfacespackages/shared/src/types/instance.ts— Contains theruntimeConfig.heartbeatsub-object with editable fieldspackages/shared/src/validators/runtime-config.ts— Validates incoming JSON against the schema
Reading and Modifying Configuration
Retrieve Current Settings
import { heartbeatsApi } from '@/api/heartbeats';
const companyId = 'c123';
const agents = await heartbeatsApi.listInstanceSchedulerAgents();
const scheduler = agents.find(a => a.companyId === companyId);
console.log('Heartbeat enabled?', scheduler?.heartbeatEnabled);
console.log('Run interval (sec)', scheduler?.intervalSec);
console.log('Last successful run', scheduler?.lastHeartbeatAt);
The listInstanceSchedulerAgents() method returns all scheduler agents visible to the authenticated session, filtered by permissions.
Enable and Tune the Scheduler
await heartbeatsApi.updateInstanceSchedulerAgent(
scheduler!.id,
{
heartbeatEnabled: true,
intervalSec: 600, // 10-minute wake-up cycle
schedulerActive: true
}
);
Changes take effect immediately. The server reconfigures its timer without requiring restart. Validation in runtime-config.ts rejects invalid combinations (e.g., negative intervals).
Inspect Live Run State
const runs = await heartbeatsApi.liveRunsForCompany(companyId);
const activeRun = runs.find(r => r.status === 'running');
if (activeRun) {
console.log('Run ID:', activeRun.heartbeatRunId);
console.log('Liveness:', activeRun.runLivenessState);
console.log('Invoked via:', activeRun.heartbeatInvocationSource);
if (activeRun.heartbeatRunOutputSilence) {
console.log('Silence warnings:', activeRun.heartbeatRunOutputSilence.level);
}
}
The liveRunsForCompany() endpoint returns full runtime state including silence detection data used for operational monitoring.
Triggering Runs Manually
For testing or ad-hoc agent activation, bypass the timer:
await heartbeatsApi.triggerRun({
companyId,
issueId: 'PAP-101', // Optional: focus on specific issue
source: 'manual', // 'timer' | 'manual' | 'api'
triggerDetail: {
reason: 'deployment-verification',
requestedBy: 'admin-007'
}
});
Manual triggers respect maxConcurrentRuns and populate the same heartbeatRunId tracking as automatic runs.
UI Administration
Administrators configure heartbeat settings in ui/src/pages/InstanceSettings.tsx, which:
- Reads current values via
heartbeatsApi.listInstanceSchedulerAgents() - Renders status pills from
runLivenessState - Writes changes through
updateInstanceSchedulerAgent() - Displays
lastHeartbeatAtas relative health indicators
The UI separates heartbeatEnabled (system capability) from schedulerActive (operational state), allowing emergency pauses without configuration loss.
Summary
- Enablement control:
heartbeatEnabledis the master switch;schedulerActiveallows runtime pausing - Frequency tuning:
intervalSecsets wake-up cadence in seconds (default 900) - Resource protection:
maxConcurrentRunscaps parallel execution at 2 - Observability: Runtime fields (
heartbeatRunId,runLivenessState,heartbeatRunOutputSilence) provide full traceability - Type safety: All options validated in
packages/shared/src/validators/runtime-config.ts
Frequently Asked Questions
What happens when heartbeatEnabled is set to false?
The scheduler stops queuing timer-driven runs entirely. Existing runs complete, but no new automatic cycles begin. Manual triggers via triggerRun() still function, allowing ad-hoc agent execution without re-enabling the system.
How does maxConcurrentRuns protect against resource exhaustion?
When a heartbeat run stalls (infinite loop, blocked I/O), additional timer ticks would normally queue overlapping runs. maxConcurrentRuns: 2 prevents more than two simultaneous runs per company, containing the blast radius until the watchdog detects the stall via lastHeartbeatAt age.
Can I change the interval without restarting Paperclip?
Yes. The updateInstanceSchedulerAgent() API applies intervalSec changes immediately. The server recalculates the next wake-up time from the current moment, not the previous schedule. This is implemented in the server-side scheduler without process restart.
What is the difference between heartbeatRunOutputSilence and runLivenessState?
runLivenessState tracks coarse execution phase (queued → running → completed/failed). heartbeatRunOutputSilence monitors fine-grained I/O health during the running phase, detecting when agents produce no output for configured thresholds. A run can be running while flagged for silence, triggering UI warnings before eventual failure.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →