Skip to content

feat: /heartbeat — recurring session re-entry prompt fired when idle - #79681

Merged
teknium1 merged 1 commit into
mainfrom
hermes/session-heartbeat
Aug 6, 2026
Merged

feat: /heartbeat — recurring session re-entry prompt fired when idle#79681
teknium1 merged 1 commit into
mainfrom
hermes/session-heartbeat

Conversation

@teknium1

@teknium1 teknium1 commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Summary

/heartbeat every <interval> <prompt> gives a session one recurring instruction that re-enters the conversation as a normal user turn whenever the session is idle and the interval has elapsed. Adapted from the session-heartbeat concept in Prime Intellect's Prime-Agent (/heartbeat).

P4 of tracker #79686closes #62648.

Deltas vs the tracker's proposed shape (for review)

  • First cut is idle-delivery only: a due tick on a busy session coalesces to the next idle poll. The tracker's steer/follow-up delivery modes can layer on once P1 establishes that vocabulary — the injection point (adapter FIFO / _pending_input) is mode-agnostic.
  • Missed ticks coalesce (anchor resets on fire) and state persists on session rows exactly as proposed; goal-judge double-trigger is avoided structurally (the heartbeat turn is an ordinary user turn — one turn, one judge pass).
  • Agent-created heartbeats deliberately out of scope, per the tracker's prompt-injection concern.

How it works

  • Idle-only injection. CLI: a daemon watchdog thread (wake-word watchdog pattern) polls and puts the prompt into _pending_input; gateway: one gateway-wide async poller injects through the adapter FIFO. A busy session coalesces its tick to the next idle poll — heartbeats never interrupt a running turn.
  • Missed ticks coalesce. The anchor resets on every fire, so a busy hour (or a stopped process) yields ONE heartbeat turn, never a backlog. Real user messages always take priority.
  • Cache- and alternation-safe. The injected prompt is a plain user-role message; no system-prompt mutation, no toolset change. It carries a "don't invent work" guard so idle heartbeats reply briefly instead of generating busywork.
  • Persistence. State lives in SessionDB.state_meta (heartbeat:<session_id>), survives /resume, and migrates across compression session rotations alongside /goal state.
  • Scope is deliberate. Session-scoped and in-process; durable cross-process scheduling stays with hermes cron. The docs page draws that boundary explicitly (heartbeat = this conversation's context, cron = isolated jobs).
  • 60s interval floor; /hb alias; Slack routes via /hermes heartbeat (50-slash cap). The ghost-text suggester now prefers the shortest prefix match so /he still suggests /help rather than /heartbeat.

Changes

  • hermes_cli/heartbeat.py (new): interval parsing (10m, every 2h, …), HeartbeatState/HeartbeatManager, due/coalesce logic, SessionDB persistence, rotation migration.
  • cli.py: _get_heartbeat_manager, _start_heartbeat_watchdog, dispatch. hermes_cli/cli_commands_mixin.py: /heartbeat handler.
  • gateway/run.py / gateway/slash_commands.py: handler for all platforms, watch registry + poller, mid-run dispatch.
  • agent/conversation_compression.py: heartbeat migration at session rotation (next to goal migration).
  • hermes_cli/commands.py: CommandDef (+/hb), Slack via-/hermes routing, shortest-prefix ghost text.
  • Docs: new heartbeat.md (registered in sidebars.ts), slash-command reference (both tables).
  • tests/hermes_cli/test_heartbeat.py: 21 tests.

Validation

Check Result
New tests (interval parsing, due/coalesce/re-anchor, lifecycle, persistence, migration) 21/21 pass
Adjacent (test_commands.py incl. Slack/Telegram parity + ghost text, test_goals.py) 93/93 pass
E2E (isolated HERMES_HOME, real SessionDB): set → due fire → fire_count persisted across manager instances → no double-fire → rotation migration pass

Infographic

session heartbeat

/heartbeat every <interval> <prompt> gives the current session one
recurring instruction. When the session is idle and the interval has
elapsed, the prompt is injected as a plain user turn — same
conversation, same context, prompt cache and role alternation
untouched.

- CLI: idle-poll watchdog thread (wake-word watchdog pattern) feeding
  _pending_input; gateway: single gateway-wide async poller injecting
  through the adapter FIFO. Busy sessions coalesce their tick to the
  next idle poll.
- Missed ticks coalesce (anchor resets on fire) — a busy hour yields
  ONE heartbeat turn, never a backlog. Real user messages always win.
- 60s interval floor; injected prompt carries a don't-invent-work
  guard so idle heartbeats don't generate busywork.
- State persists in SessionDB.state_meta (heartbeat:<session_id>),
  survives /resume, migrates across compression session rotations
  alongside /goal state.
- Session-scoped and in-process by design — durable cross-process
  schedules remain the cron subsystem's job (docs draw the boundary).
- Slack stays under the 50-slash cap via /hermes heartbeat; ghost-text
  suggester now prefers the shortest prefix match so /he still
  suggests /help.

Adapted from the session-heartbeat concept in Prime Intellect's
Prime-Agent (/heartbeat).
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

૮ >ﻌ< ა ci review

ran on ba2ae96

⚠️ Warnings

OSV vulnerability scan · View job

37 known vulnerabilities found in pinned dependencies.

How to fix:

Review the findings in the Security tab. Update the affected dependencies if a patched version is available.


debug info

CI timings

CI timings · View report · View job

Wall time 8m11s vs 11m21s (-27.9%). 12 job(s) slower, 8 faster,

  • Python tests / Run tests slice 2/8: +146.0s
  • Check contributors / check-attribution: +37.0s
  • Python tests / Run tests slice 5/8: +33.0s
  • Check uv.lock / uv lock --check: +15.0s
  • Python tests / Run tests slice 7/8: -12.0s

@alt-glitch alt-glitch added type/feature New feature or request comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cli CLI entry point, hermes_cli/, setup wizard comp/gateway Gateway runner, session dispatch, delivery area/sessions Session lifecycle, resume, persistence, history P3 Low — cosmetic, nice to have sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages labels Aug 5, 2026
@teknium1
teknium1 merged commit 6518aa1 into main Aug 6, 2026
45 checks passed
@teknium1
teknium1 deleted the hermes/session-heartbeat branch August 6, 2026 05:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/sessions Session lifecycle, resume, persistence, history comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cli CLI entry point, hermes_cli/, setup wizard comp/gateway Gateway runner, session dispatch, delivery P3 Low — cosmetic, nice to have sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Session-internal heartbeat / activity interval tracker

2 participants