Agent Self-Scheduling
Make an agent run periodically. First question: does the agent have a built-in scheduler, or do you own the clock?
Decision: pick the camp first
- Need it NOW, agent has no scheduler, or sub-minute polling → Camp A (external clock).
- Want managed recurring jobs, isolated sessions, chaining, loop safety → Camp B (Hermes built-in).
Universal floor: ALL cron is 1 minute (5-field expr, no seconds) — every camp. For sub-minute / sub-second you MUST use a dumb while ...; sleep N; done loop, a TS extension, or a webhook/event hook. Never put an LLM on a tight timer.
Camp A — One-shot agents, you own the clock
These run once and exit (amnesiac unless resumed). You schedule them externally.
One-shot invocations:
bashclaude -p "PROMPT" --output-format json --allowedTools "Read,Edit,Bash" # Claude Code codex exec --json "PROMPT" # Codex pi run "PROMPT" # Pi.dev (Mario Zechner; MIT; read/write/edit/bash + TS SDK)
Wrap in a clock — three options:
bash# 1. cron (>= 1 min). Floor is 1 minute, 5 fields, NO seconds. */10 * * * * cd /path/to/project && pi run "check X and report" >> ~/agent.log 2>&1 # 2. systemd timer (Linux, survives reboot, better logging) — OnUnitActiveSec=10min # 3. dumb loop (sub-minute, or when cron unavailable) while true; do pi run "check X"; sleep 30; done
Camp A gotchas (all three break unattended runs if ignored):
- Permissions block unattended runs. Pass
--allowedTools(Claude) or a sandbox/auto-approve flag (Codex) or the run hangs on a prompt forever. - Use structured JSON output, not prose (
--output-format json/--json) so the wrapper can parse the result deterministically. - Each run is amnesiac. No memory across runs unless you resume:
codex exec resume --last. Otherwise persist state to a file the next run reads.
Pi specifics: Pi has NO built-in loop/scheduler/heartbeat by design — only the agent reasoning loop (not time-based). OpenClaw's runtime is built ON Pi but is a separate project. To schedule Pi: external cron/systemd, a while-sleep loop, or a TS extension for agent-side timers.
cmux (orchestration layer — NO scheduler)
cmux has NO scheduler/timer/watch/cron. It is visibility + orchestration only. Primitives: list-workspaces, read-screen (non-interruptive — safe to poll), send. An MCP wrapper cmux-agent-mcp exists.
Three ways to make cmux loop:
- Orchestrator-driven — the orchestrator runs
send→sleep→read-screenon its own clock. - Dumb wrapper — external
while true; ...; sleep N; done. - Event-driven (preferred over polling) — use
cmux notify+ OSC 9/99/777 terminal hooks so work fires on a real signal instead of a timer. Cheaper and more responsive than read-screen polling.
Camp B — Hermes (built-in scheduler daemon)
Hermes ships a gateway that ticks every 60s and runs due jobs in fresh, isolated sessions. No external cron needed.
State-check first — is the gateway running?
bashhermes gateway install # user-level hermes gateway install --system # survives reboot (boot-time)
Create jobs (CLI or /cron add in TUI):
bashhermes cron create "every 1h" "summarize new emails and report" --skill himalaya hermes cron create "0 9 * * *" "post daily standup" # cron expr hermes cron create "30m" "one-shot reminder in 30 min" # one-shot delay
Schedule formats: one-shot delays (30m, 2h), recurring (every 30m, every 1d), cron (0 9 * * *), ISO timestamps.
Hermes-unique capabilities:
- Zero-token / no-agent mode — run a script on schedule, deliver its stdout verbatim. No LLM cost. Use for watchdogs/heartbeats where the script produces the exact message.
- Chaining —
context_frominjects an upstream job's last output into a downstream job; combine with a working dir to pipeline jobs. - Self-terminating loops — a recurring job can stop itself when a condition is met.
- Fresh self-contained sessions — each run starts clean; the prompt must carry all needed context (no chat history).
- Loop safety — cron-run sessions CANNOT create more cron jobs (prevents runaway recursion). Don't try to schedule from inside a scheduled job.
- Persistent
/goalloop — long-running autonomous loop with pause/resume, separate from cron.
Heartbeat pattern (proactive self-check)
A heartbeat = a fast recurring tick that gates slower per-task checks, so one loop handles many intervals at different cadences.
- In Camp B: a recurring Hermes job (e.g.
every 5m) whose prompt/script checks a task list and only acts on tasks whose own interval is due. Use no-agent mode + a script for zero token cost when nothing's due. - In Camp A: same idea via a while-sleep loop that reads a tasks file and per-task
last_runtimestamps. - Save tokens: define active-hours and skip ticks outside them; skip the tick entirely when there's nothing due (stay silent — don't emit empty noise).
Verify it actually fires (do this before reporting success)
- Camp A: check the log file grows after one interval, or run the wrapped command once by hand and confirm clean JSON exit (0).
- Camp B:
hermes cron listshows the job; checknext_run; trigger once with a run-now to confirm it executes and delivers. - Confirm permissions/sandbox flags are present (Camp A) — the #1 silent failure is a hung permission prompt.
- For heartbeats, confirm a tick with nothing-due stays silent (no spam).
Pitfalls
- Cron CANNOT do sub-minute. Don't promise
every 30svia crontab — use a loop. - Camp A unattended hangs without explicit tool allowlists / sandbox.
- Don't schedule a job from inside a scheduled Hermes job (loop safety blocks it).
- Each scheduled run is a fresh/amnesiac session — pass all context in the prompt or persist to a file.


