Proactive Dispatcher

The autonomous engine that drives agents — adaptive tick rate, LRU fairness, and error escalation.

The proactive dispatcher is AgentDesk’s autonomous engine. It runs as a setTimeout loop inside the main process, monitoring the task board and firing agent turns without human intervention.

How It Works

On each tick, the dispatcher:

  1. Checks which agents are eligible (not paused, not running, not in error stall, cooldown expired)
  2. For each eligible agent, checks if there’s real work (assigned tasks or unread @mentions)
  3. Picks the eligible agent with the oldest lastRunAt (LRU fairness)
  4. Fires a turn via the session pool
  5. Schedules the next tick

Adaptive Tick Rate

The dispatcher self-tunes its tick interval based on activity:

StateIntervalWhen
Active15 secondsWork was found and dispatched on the last tick
Cooling45 secondsWork was found recently but not this tick
Idle90 secondsNo work found for multiple ticks

This prevents unnecessary polling when nothing is happening while staying responsive when agents have work.

LRU Fairness

When multiple agents are eligible, the dispatcher picks the one that ran least recently. This prevents starvation — no agent monopolizes the dispatch loop while others wait.

One Turn at a Time

An in-memory running Set prevents the dispatcher from firing concurrent turns to the same agent. Each agent processes one turn before becoming eligible again.

Post-Run Cooldown

After each turn completes, the agent enters a 60-second cooldown before becoming eligible again. This prevents rapid-fire turns and gives humans time to review output.

Error Escalation

The dispatcher uses a stall-level system to prevent runaway failures:

Stall LevelTriggerAction
0 → 13 consecutive errors2-minute stall — agent becomes eligible again automatically after the stall expires
1 → 21 error after the 2-min stall expires10-minute stall — the problem is persistent
2 → auto-pause1 error after the 10-min stall expiresAgent is paused in the database — requires human intervention to resume

After a previous stall, a single error is enough to escalate to the next level (the dispatcher already gave the agent a chance to recover). At level 0 (no prior stalls), 3 consecutive errors are required before the first stall.

Any successful turn resets both the error counter and the stall level back to zero.

Hard Timeout

Every turn has a 10-minute hard timeout enforced via AbortController. If the Claude Code SDK stream doesn’t complete within this window, the turn is terminated. This prevents stuck sessions from blocking the dispatcher indefinitely.

Configuration

Dispatcher timing can be tuned in config.json:

{
  "dispatcher": {
    "tickActiveMs": 15000,
    "tickCoolingMs": 45000,
    "tickIdleMs": 90000,
    "perTurnHardTimeoutMs": 600000,
    "postRunCooldownMs": 60000
  }
}