fix(notify): scope sentinel per-session; dedupe consume; tidy

Builds on PCinkusz's /notify command (previous commit) to fix one design
flaw and tighten the implementation:

- Per-session sentinel. The pending-notify flag was a single global file
  (~/.hermes/.notify_pending). The TUI gateway and dashboard serve many
  sessions from one process sharing one HERMES_HOME, so a /notify set in
  session A would fire on session B's next turn completion. Key the sentinel
  by HERMES_SESSION_KEY (resolved from the per-turn contextvar in the gateway,
  the slash worker's env, or os.environ in the classic CLI). Classic
  single-session CLI keeps the unsuffixed default file — no behavior change.
- Single consume helper. The check->clear->fire block was copy-pasted at four
  sites (2 in cli.py, 2 in tui_gateway/server.py). Extract
  consume_pending_notification(session_key) and call it everywhere; the TUI
  sites pass session["session_key"] explicitly since that process has no
  per-session contextvar bound at the consume point.
- Drop the unused config= param from fire_notification; add the missing
  trailing newline; reuse approval._get_session_platform() in the
  approval-notify guard.
- tests/tools/test_notify_utils.py: per-session isolation, consume
  fire-once/scope, default-key, env-resolution.

Co-authored-by: PCinkusz <pcinkusz123321@gmail.com>
This commit is contained in:
Brooklyn Nicholson 2026-06-18 13:52:15 -05:00
parent eb20289f96
commit c4aeb8a931
4 changed files with 172 additions and 53 deletions

20
cli.py
View file

@ -10442,14 +10442,8 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin):
# Must run BEFORE goal continuation so the notification
# reflects actual turn completion (mirrors the TUI path).
try:
from tools.notify_utils import (
is_notify_pending,
clear_notify_flag,
fire_notification,
)
if is_notify_pending():
clear_notify_flag()
fire_notification()
from tools.notify_utils import consume_pending_notification
consume_pending_notification()
except Exception as e:
logging.debug("notify idle-check failed: %s", e)
@ -13131,14 +13125,8 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin):
# reflects the actual turn completion, not a
# potentially-auto-continued turn.
try:
from tools.notify_utils import (
is_notify_pending,
clear_notify_flag,
fire_notification,
)
if is_notify_pending():
clear_notify_flag()
fire_notification()
from tools.notify_utils import consume_pending_notification
consume_pending_notification()
except Exception as e:
logging.debug("notify idle-check failed: %s", e)