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:
- Checks which agents are eligible (not paused, not running, not in error stall, cooldown expired)
- For each eligible agent, checks if there’s real work (assigned tasks or unread @mentions)
- Picks the eligible agent with the oldest
lastRunAt(LRU fairness) - Fires a turn via the session pool
- Schedules the next tick
Adaptive Tick Rate
The dispatcher self-tunes its tick interval based on activity:
| State | Interval | When |
|---|---|---|
| Active | 15 seconds | Work was found and dispatched on the last tick |
| Cooling | 45 seconds | Work was found recently but not this tick |
| Idle | 90 seconds | No 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 Level | Trigger | Action |
|---|---|---|
| 0 → 1 | 3 consecutive errors | 2-minute stall — agent becomes eligible again automatically after the stall expires |
| 1 → 2 | 1 error after the 2-min stall expires | 10-minute stall — the problem is persistent |
| 2 → auto-pause | 1 error after the 10-min stall expires | Agent 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
}
}