Files
hermes-agent/website/docs/user-guide/features/loops.md
teknium1 f79440e0f4 feat: /loop — recurring in-session wakeups (Claude Code parity)
Ports Claude Code's /loop (and its /proactive alias) across every Hermes
surface. /loop [interval] <prompt> re-runs a prompt or slash command on a
recurring cadence inside the live session; omitting the interval enables
self-paced mode (starts at the floor, backs off exponentially while the
agent's replies stop changing, snaps back on change — local digest
comparison, zero extra LLM cost).

Stop conditions: agent-emitted LOOP_COMPLETE marker, --times N,
--until <condition> (judged by the existing goal_judge aux task,
fail-open), /loop stop, and a loops.max_ticks backstop budget.

Core: hermes_cli/loops.py (LoopState + LoopManager + shared
dispatch_loop_command), persisted per session in SessionDB state_meta
(loop:<sid>) so /resume picks it up; migrates across compression
boundaries like /goal. New SessionDB.list_meta_prefix() powers the
gateway's cross-session scan.

Surfaces:
- CLI: /loop handler + idle-fire and post-turn-complete hooks in
  process_loop (mirrors the /goal hook shape; Ctrl+C pauses the loop)
- Gateway: /loop handler with route capture, mid-run control-verb guard,
  post-turn tick completion, and a supervised loop_wakeup_watcher that
  injects due wakeups into idle chats via the synthetic-message path
- TUI/dashboard/desktop: command.dispatch handler + per-session
  notification-poller wakeup driver + post-turn completion in the turn
  dispatcher; /loop added to the desktop slash palette
- /goal mixing: an active non-parked goal owns the idle boundary — loop
  ticks defer until it finishes, pauses, or parks; real user input always
  wins over both

Config: loops.{min_interval_seconds,max_ticks,self_paced_floor_seconds,
self_paced_ceiling_seconds}. Docs page + sidebar entry. 77 new tests.
Slack's 50-slash cap: /version moves to /hermes version to free the
native slot for /loop.
2026-08-14 13:40:19 -07:00

6.8 KiB

sidebar_position, title, description
sidebar_position title description
17 Recurring Loops Re-run a prompt on a recurring interval inside your session — Hermes' take on Claude Code's /loop.

Recurring Loops (/loop)

/loop re-runs a prompt (or a slash command) on a recurring cadence inside your current session. Each wakeup is a real agent turn: Hermes reads the current state fresh — the latest CI result, the newest queue depth, the file as it is now — does the work, reports back, and goes quiet until the next tick.

It's Hermes' take on Claude Code's /loop (and its /proactive alias, which works here too). Where /goal is judge-driven — "keep working until this objective is achieved" — /loop is timer-driven: "do this again every N minutes (or whenever it makes sense) until something says stop."

When to use it

  • Polling external state. "Watch the deploy / the CI run / the queue and tell me when it changes." The canonical use case.
  • Iterate-until-green. "Run the tests, fix what fails, repeat until they pass."
  • Monitoring during a work session. Keep an eye on error rates or a long job's progress while you do something else in the same conversation.
  • Periodic housekeeping. Re-run a lint pass or a status summary every N minutes during a long session.

When the work should run unattended — overnight, on a real schedule, surviving restarts of your terminal — use a cron job instead. /loop lives inside a session; cron lives outside all of them. And when the task is a single objective with a definition of done, /goal is usually the better fit.

Quick start

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

What you'll see:

  1. Loop accepted — ↻ Loop set (every 5m): check the deploy status…
  2. First wakeup in 5m — while the session is idle, Hermes injects the wakeup and runs a normal turn against current state.
  3. Repeat — every 5 minutes, until a stop condition fires or you stop it.

Loop a slash command just as easily:

/loop 10m /recap

The two cadence modes

Fixed interval — you set the clock. Give an interval (30s, 5m, 2h, 1h30m) and the loop fires on that schedule. Use it when the thing you're watching changes on its own timeline:

/loop 2m poll the build at ci.example.com/job/42 and ping me the moment it finishes

Self-paced — Hermes sets the clock. Omit the interval and the loop paces itself: it starts at the floor (1 minute by default), and while the agent's replies stop changing it backs off exponentially — 2m, 4m, 8m, up to the ceiling (15 minutes by default). The moment a reply differs from the last one, cadence snaps back to the floor. Change detection is a local digest comparison (timestamps are ignored), so idle waits cost nothing extra:

/loop keep an eye on the migration and summarize progress

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

Stop conditions

A loop ends when any of these fires:

Condition How
The agent decides it's done The wakeup prompt teaches the agent to end its reply with LOOP_COMPLETE on its own line when the task is finished or moot.
A run cap --times N — stop after N wakeups.
An evidence-based condition --until <condition> — after each wakeup, the same auxiliary judge that powers /goal checks the reply against your condition (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) pauses the loop so an unattended session can't burn tokens forever. 0 = unlimited.

Examples:

/loop 2m poll CI --times 30
/loop 5m watch the queue --until queue depth reaches zero

Commands

Command What it does
/loop [interval] <prompt> [--times N] [--until <cond>] Start (or replace) the loop for this session.
/loop or /loop status Show cadence, ticks fired, and time to the next wakeup.
/loop pause Stop firing without losing the loop.
/loop resume Pick it back up.
/loop stop End the loop.
/proactive … Alias for /loop (Claude Code parity).

Works on the CLI, the TUI (hermes --tui), the web dashboard chat, the desktop app, and every gateway platform (Telegram, Discord, Slack, WhatsApp, …). On messaging platforms the gateway fires wakeups even between your messages — the loop belongs to the chat's session, and its results arrive as ordinary replies.

Mixing with /goal

Both features inject synthetic turns at idle boundaries, so they follow one rule: an active goal owns the session. While a /goal is actively driving (judge saying "continue"), loop wakeups defer. The loop resumes using idle time as soon as the goal finishes, pauses, or parks itself on a wait barrier (/goal wait, or the judge's automatic WAIT verdict). A parked goal plus a /loop is a natural combo: the goal waits on the big async thing while the loop keeps a heartbeat on something else.

A real user message always wins over both — wakeups only fire while the session is idle and nothing of yours is queued.

Behavior details

  • A wakeup is a normal user-role turn. No system-prompt mutation, no toolset swap — prompt caching stays intact.
  • Survives /resume and compression. Loop state persists per session and migrates across context-compression boundaries, same as /goal.
  • One loop per session. Setting a new /loop replaces the old one. Run several loops by running several sessions (or use cron for a fleet of schedules).
  • Interrupting a wakeup turn (Ctrl+C) pauses the loop — recoverable with /loop resume, so cancel actually means cancel.
  • Token cost scales with cadence. Every tick is a full agent turn. Match the interval to how often the state actually changes; prefer self-pacing for idle waits.

Configuration

# ~/.hermes/config.yaml
loops:
  min_interval_seconds: 30       # floor for fixed intervals
  max_ticks: 100                 # backstop budget (0 = unlimited)
  self_paced_floor_seconds: 60   # self-paced starting cadence
  self_paced_ceiling_seconds: 900  # self-paced max backoff

The --until judge routes through the goal_judge auxiliary task, so auxiliary.goal_judge.* overrides (provider, model, max_tokens) apply to loop conditions too.

/loop vs /goal vs cron

/loop /goal cron
Trigger Timer (or self-paced) Judge verdict after each turn Schedule, outside any session
Lives in Your current session Your current session Its own session per run
Ends when Stop condition / caps / you Goal achieved / budget / you You remove the job
Best for Polling, monitoring, periodic re-runs One objective, iterate until done Unattended, long-horizon schedules