/loop: Hermes' Recurring In-Session Wakeups — Timer-Driven, Not Judge-Driven, Not Cron


The deploy script finished, but you can’t walk away — will CI go red? Will the queue back up? When is the service actually live? So you refresh the page every five minutes like a dutiful sentry.

Hermes’ newest /loop command exists to take that sentry shift off your hands: re-run a prompt on a recurring cadence inside your session, waking up each tick to work against current state. It’s Hermes’ take on Claude Code’s /loop (the /proactive alias works here too), just merged into main. This post explains it properly: what fundamentally separates it from /goal and cron, and how to actually use it.

Three commands, three drivers

Command Driver Lifecycle Typical use
/goal Judge-driven — after every turn a judge model checks “is it done yet?”, and Hermes keeps working if not Single session, across turns “Fix every lint error in src/ and verify the check passes”
/loop Timer-driven — wake up on cadence, do the work, until something says stop Single session, across turns and across resumes “Check the deploy every 5 minutes and tell me when it’s live”
cron Schedule-driven — fixed timetable, no session involved Outside all sessions, unattended “Post a daily digest to the channel at 9am”

/goal means “keep working until the objective is achieved,” /loop means “keep checking until something says stop,” cron means “run on schedule, unrelated to conversation.” Three non-overlapping tools.

Two cadence modes: you set the clock, or it paces itself

Fixed interval — your clock

/loop 5m check the deploy status and tell me if it's live yet

Every 5 minutes (while the session is idle) Hermes injects a real agent turn against current state — the latest CI result, the newest queue depth, the file as it is now.

Self-paced — it gets smarter the longer it waits

Omit the interval and Hermes paces itself:

/loop keep an eye on the migration and summarize progress

The mechanism is clever: it starts at the 60s floor, and while the agent’s replies stop changing it backs off exponentially (2m → 4m → 8m … up to the 15-minute ceiling). The moment a reply differs, cadence snaps back to the floor. Change detection is a local, timestamp-insensitive digest comparison — zero extra LLM cost. The calmer things are, the less it bothers you; the moment there’s progress, it leans in.

Rule of thumb: fixed interval when an external clock drives the work; self-paced when the work drives the rhythm.

Stop conditions: five exits

Condition How
The agent decides it’s done Wakeup replies ending with LOOP_COMPLETE on its own line
A run cap --times N (e.g. --times 30)
An evidence-based condition --until <condition> — judged by the same aux judge that powers /goal (fail-open: a broken judge never wedges the loop)
You /loop stop (or /loop pause to keep it around)
The backstop budget loops.max_ticks (default 100; 0 = unlimited) so an unattended session can’t burn tokens forever
/loop 2m poll CI --times 30
/loop 5m watch the queue --until "queue depth reaches zero"

Control and composition

  • /loop status shows cadence, ticks fired, and time to next wakeup; /loop pause / resume / stop cover the full lifecycle (Ctrl+C during a wakeup pauses the loop, recoverable via /loop resume).
  • Loop a slash command just as easily: /loop 10m /recap.
  • Working with /goal: an active, non-parked goal owns the idle boundary — loop ticks defer until the goal finishes, pauses, or parks (a wait barrier). A parked goal plus a loop heartbeat composes naturally. Real user input always preempts both — the moment you type, both get out of the way.

Why it doesn’t fall over: the architecture

This is the part worth really understanding — /loop is not a while true loop:

  1. Ordinary user-role turn injection: every wakeup is a normal user-role message. No system-prompt mutation, no toolset swap, prompt cache stays intact — looping doesn’t destroy your cache or waste tokens.
  2. Persisted state: loop state lives in SessionDB.state_meta under loop:<session_id>, so /resume brings the loop back, and it migrates across context compression (same mechanism as /goal, with the known hazard fixed pre-emptively).
  3. Every surface: CLI, TUI, dashboard, desktop app, and all gateway messaging platforms — a supervised loop_wakeup_watcher scans persisted loops and injects due wakeups into idle chats even while you’re away. That’s something Claude Code can’t do (its loop dies with the CLI session).
  4. The Slack aside: Slack has a 50-slash-command cap, so Hermes moved /version to /hermes version to free a native slot for /loop.

Compared with Claude Code’s /loop

Claude Code Hermes
Surfaces CLI session only CLI, TUI, dashboard, desktop, all messaging platforms
Persistence Dies with the session Survives /resume and context compression
Self-paced pacing Model-decided Local digest backoff (free, deterministic)
Stop conditions Prompt-embedded LOOP_COMPLETE + --times + judged --until + budget backstop
/goal interplay Separate features Explicit precedence: active goal owns the idle boundary

Which one do I want?

Release status and how to get it

/loop was merged into main on August 14, 2026 (PR #72333, with 77 new tests + 367 regression tests + 22 desktop tests, all passing). It is not in any official release yet (latest is still v0.20.1). To try it:

  • Wait for the next release and run hermes update;
  • Or install from main now: hermes update --branch main (switch back once the release ships).

Optional config keys: loops.min_interval_seconds, loops.max_ticks, loops.self_paced_floor_seconds, loops.self_paced_ceiling_seconds.

Wrap-up

/loop takes “watching” out of your manual-refresh hands: fixed intervals for external clocks, self-paced cadence that gets smarter the longer it waits (back off when stable, lean in when things change — at zero LLM cost), persistence and full-platform coverage that outdo the Claude Code original, and explicit cooperation with /goal. Next deploy, hand the sentry shift to it.