Reeve
Goals Engine

Service Loops

Fast, gateway-managed execution loops for intensive goal phases.

Service Loops

Service loops are fast, in-process execution loops managed by the gateway. They're designed for intensive phases that need to run more frequently than cron allows — think real-time monitoring, rapid iteration, or time-sensitive optimization.

How Service Loops Work

Unlike cron (which has a minimum 1-minute granularity), service loops run as setInterval timers inside the gateway process:

  1. A loop is started for a specific goal + phase
  2. Each iteration enqueues a system event to the agent's session
  3. The system event triggers a heartbeat wake — the agent processes the event
  4. The agent reads its goal context, does work, and the loop fires again

Service loops do not run agent sessions directly. They use the existing system event + heartbeat wake infrastructure to deliver work to agents.

Starting a Loop

goals({
  action: "start_loop",
  goalId: "g_abc123",
  phaseIndex: 1,          // Which phase to loop
  intervalMs: 60000,      // Every 60 seconds
  maxRuntimeMs: 28800000  // Stop after 8 hours (optional)
})

Parameters

ParameterTypeDefaultDescription
goalIdstringrequiredGoal to loop
phaseIndexnumber0Phase index within the goal
intervalMsnumber300000 (5m)Interval between iterations
maxRuntimeMsnumber28800000 (8h)Maximum total runtime

Constraints

  • Minimum interval: 10 seconds (MIN_INTERVAL_MS = 10_000)
  • Maximum runtime: 8 hours default (DEFAULT_MAX_RUNTIME_MS)
  • One loop per goal: Only one service loop can be active per goal ID
  • Goal must exist: The goal must be in the store and have the specified phase

Stopping a Loop

goals({ action: "stop_loop", goalId: "g_abc123" })

Loops also stop automatically when:

  • The maxRuntimeMs is reached (kill timer fires)
  • The goal is paused, failed, cancelled, or completed
  • The gateway restarts (loops are in-memory only)

Loop State

Each active loop tracks:

{
  running: true,
  iterations: 42,
  startedAt: "2026-02-27T12:00:00Z",
  nextAtMs: 1740664800000  // Next iteration timestamp
}

Service Loop vs Cron

FeatureService LoopCron
Minimum interval10 seconds1 minute
Maximum runtime8 hours (configurable)Unlimited
Survives restartNo (in-memory)Yes (persisted)
Managed bygoals/service-loop.jsgoals/scheduler.js
Best forIntensive, time-limited phasesLong-running periodic work

Use service loops when:

  • You need sub-minute granularity
  • The phase is time-boxed (monitoring during a launch, etc.)
  • Rapid iteration is important

Use cron when:

  • The phase runs indefinitely (daily reports, weekly checks)
  • You need to survive gateway restarts
  • Interval is 1 minute or longer

Architecture

Service Loop Timer (setInterval)
  └─ enqueueSystemEvent(agentId, "goal_loop_iteration", { goalId, phase, iteration })
       └─ requestHeartbeatNow(agentId)
            └─ Agent wakes up
                 └─ Reads goal context (injected by heartbeat-hook)
                      └─ Does work, updates metrics, checkpoints

The indirection through system events is important — it means the loop doesn't hold a reference to the agent session. The agent processes the event in its normal message handling flow.

Service loops are in-memory only and don't survive gateway restarts. For production workloads that must persist, use cron-based loop phases instead. The gateway's startup sync in goals/scheduler.js re-creates cron jobs for active loop phases but does not restart service loops.

Example: Real-Time Ad Monitoring

// Create goal with monitoring phase
goals({
  action: "create",
  goal: {
    title: "Launch day ad monitoring",
    agentId: "ad-ops",
    phases: [
      { name: "Pre-launch check", type: "once" },
      { name: "Real-time monitoring", type: "loop" },
      { name: "Post-launch report", type: "once" }
    ],
    budget: { maxCost: 25, approvalGate: 15 }
  }
})

// After pre-launch phase completes, start the monitoring loop
goals({ action: "complete_phase", goalId: "g_abc123" })
goals({
  action: "start_loop",
  goalId: "g_abc123",
  phaseIndex: 1,
  intervalMs: 30000,       // Every 30 seconds
  maxRuntimeMs: 14400000   // 4 hours
})

// Agent receives system events every 30s, checks ad metrics,
// adjusts bids, pauses underperformers, etc.
// After 4 hours (or manual stop), complete the monitoring phase
goals({ action: "stop_loop", goalId: "g_abc123" })
goals({ action: "complete_phase", goalId: "g_abc123", result: "4h monitoring complete. CPA reduced 18%." })

On this page