hermes - ✅(Solved) Fix TUI: notify_on_complete silently drops — no notification delivered for background terminal processes [1 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
NousResearch/hermes-agent#15248Fetched 2026-04-25 06:23:27
View on GitHub
Comments
0
Participants
1
Timeline
5
Reactions
0
Participants
Timeline (top)
labeled ×4cross-referenced ×1

When using terminal(background=true, notify_on_complete=true) from a TUI session, the completion notification is never delivered. The same code path works correctly in Discord sessions.

Root Cause

When using terminal(background=true, notify_on_complete=true) from a TUI session, the completion notification is never delivered. The same code path works correctly in Discord sessions.

Fix Action

Workaround

Use process(action='poll', session_id=...) explicitly. Works fine but requires a prompt from the user.

PR fix notes

PR #15329: fix(tui): deliver notify_on_complete process notifications to TUI sessions

Description (problem / solution / changelog)

Summary

TUI sessions silently dropped notify_on_complete process notifications while Discord/Telegram/gateway sessions delivered them correctly. The TUI gateway (tui_gateway/server.py) had no consumer for process_registry.completion_queue, so background process completion events queued up and were never dispatched to any active TUI session.

Root Cause

The gateway (gateway/run.py) handles completion notifications by:

  1. Draining process_registry.completion_queue in the post-agent-loop phase
  2. Formatting events via _format_gateway_process_notification()
  3. Injecting them as synthetic [SYSTEM:] prompts

The TUI gateway runs a completely separate code path — it never touches completion_queue at all.

Fix

Add a daemon worker thread (_process_notification_loop) that:

  • Drains completion_queue for completion, watch_match, and watch_disabled events
  • Resolves the owning TUI session via session_key lookup in process_registry
  • Waits for session readiness — skips if the session isn't in the ready state or is currently busy (agent turn in progress)
  • Skips already-consumed completions — checks is_completion_consumed before dispatching completions previously returned by explicit wait/poll/log calls
  • Formats and injects the notification as a synthetic prompt.submit JSON-RPC message, exactly mirroring how the gateway delivers them

The worker is lazily started by _ensure_process_notification_worker(), called from both _init_session and the session.create RPC handler. It is started lazily when a TUI session exists and remains as a process-lifetime daemon, sleeping while there are no active sessions.

Changes

FileChange
tui_gateway/server.pynotification loop, session resolver, lazy starter, synthetic request IDs
tests/test_tui_gateway_server.pyregression tests covering full round-trip, consumed-completion suppression, and synthetic request IDs

How to Test

  1. Start a TUI session: hermes --tui
  2. Run a background process with completion notification:
    /terminal background=true notify_on_complete=true -- python -c 'import time; time.sleep(3); print("done")'
  3. After ~3 seconds, the TUI Activity panel should show:
    [SYSTEM: Background process <sid> finished.
    Command: python -c ...
    Exit code: 0]
  4. The agent should then start a new turn processing that notification.

Before this fix: nothing happened — the completion was silently dropped.

Validation

  • Focused tests: pytest tests/test_tui_gateway_server.py::test_tui_process_completion_notification_starts_agent_turn tests/test_tui_gateway_server.py::test_tui_process_notification_rechecks_consumed_after_wait tests/test_tui_gateway_server.py::test_tui_process_notification_uses_synthetic_request_id — 3 passed
  • Related area tests: 87 passed across test_tui_gateway_server.py, tests/tools/test_notify_on_complete.py, tests/gateway/test_internal_event_bypass_pairing.py
  • Lint/format: ruff check and format — only pre-existing violations (E402, E731, E401, F401) that reproduce identically on clean origin/main
  • Full suite: 18 failed, 15,468 passed — all 18 failures reproduce on a clean origin/main checkout (pre-existing, unrelated to this diff)

Pre-existing failures confirmed on origin/main

Test fileRoot cause
test_subagent_stop_hook.py (6)Hook registration mismatch
test_delegate.py (6)Codex credential pool not seeded in hermetic env
test_custom_provider_model_switch.py (1)fetch_api_models signature drift
test_provider_config_validation.py (2)Log capture assertion
test_plugin_scanner_recursion.py (1)Kind fallback assertion
test_hindsight_provider.py (1)Key preservation
test_web_server.py (1)Schema category count
test_claw.py (3)TTY detection in non-interactive env

Risk Classification

Primary: local_small — localized bugfix in tui_gateway/server.py only, no shared helpers or config touched.

Secondary: test_isolation — the regression test spawns a daemon thread within the test process; queue/session cleanup covered in the regression test.

Fixes #15248

Changed files

  • tests/test_tui_gateway_server.py (modified, +200/-0)
  • tui_gateway/server.py (modified, +145/-1)

Code Example

terminal(
    command="some-long-running-command",
    background=True,
    notify_on_complete=True,
)

---

2026-04-23 03:05:48,906 INFO gateway.run: Process proc_35b09442b7c3 finished — injecting agent notification for session agent:main:discord:thread:1496700979008245910:1496700979008245910 ...
2026-04-23 03:24:54,686 INFO gateway.run: Process proc_b5089ea8ab48 finished — injecting agent notification for session agent:main:discord:thread:1496700979008245910:1496700979008245910 ...
RAW_BUFFERClick to expand / collapse

Summary

When using terminal(background=true, notify_on_complete=true) from a TUI session, the completion notification is never delivered. The same code path works correctly in Discord sessions.

Environment

  • Transport: hermes --tui (Ink React terminal UI via tui_gateway)
  • Version: Hermes main, observed 2026-04-24
  • Model: claude-opus-4-7

Reproduction

From a TUI session:

terminal(
    command="some-long-running-command",
    background=True,
    notify_on_complete=True,
)

Let the process exit. No injected agent turn / notification ever arrives. Observed three times in a single session with subagent CLIs (hephaestus chat -q ..., calliope chat -q ...) — all three exited cleanly with exit code 0, none produced a notification. process(action='poll', session_id=...) confirmed status: exited each time.

Evidence (~/.hermes/logs/agent.log)

For Discord sessions, the gateway logs the injection:

2026-04-23 03:05:48,906 INFO gateway.run: Process proc_35b09442b7c3 finished — injecting agent notification for session agent:main:discord:thread:1496700979008245910:1496700979008245910 ...
2026-04-23 03:24:54,686 INFO gateway.run: Process proc_b5089ea8ab48 finished — injecting agent notification for session agent:main:discord:thread:1496700979008245910:1496700979008245910 ...

For TUI sessions, grep -i "notify\|notification" agent.log returns zero "injecting agent notification" entries despite multiple qualifying background processes in the same timeframe. The injection path in gateway/run.py appears to lack a route for the TUI transport (or the TUI backend in tui_gateway/ doesn't register for process-completion callbacks the way Discord/Telegram platform adapters do).

Expected Behavior

On process exit, the TUI receives a new agent turn (or equivalent UI signal) so the agent can act on the completion without the user needing to ask. Parity with Discord/Telegram notification delivery.

Actual Behavior

Silent drop. User has to ask "did it finish?", agent polls manually. This defeats the purpose of notify_on_complete=true and makes parallel subagent workflows awkward in the TUI specifically.

Workaround

Use process(action='poll', session_id=...) explicitly. Works fine but requires a prompt from the user.

Suggested Investigation

  1. gateway/run.py — find the process-completion hook that emits the "injecting agent notification" log line; check whether it has a TUI branch or a transport-agnostic injection path.
  2. tui_gateway/ — verify it subscribes to whatever event bus fires on background-process exit.
  3. Possible fix: route TUI notifications through the same JSON-RPC channel used for model tokens, with a distinct message type so the Ink frontend can render them as an unprompted agent turn.

Happy to attach a full agent.log snippet or repro transcript if useful.

extent analysis

TL;DR

The most likely fix involves modifying the gateway/run.py to include a TUI branch for process-completion hooks and ensuring tui_gateway/ subscribes to the relevant event bus for background-process exit events.

Guidance

  • Investigate the gateway/run.py file to find the process-completion hook that emits the "injecting agent notification" log line and check if it has a TUI branch or a transport-agnostic injection path.
  • Verify that tui_gateway/ subscribes to the event bus that fires on background-process exit, potentially requiring changes to subscribe to this event.
  • Consider routing TUI notifications through the same JSON-RPC channel used for model tokens, with a distinct message type for the Ink frontend to render them as an unprompted agent turn.
  • Review the tui_gateway/ code to ensure it properly handles and forwards notifications to the TUI session.

Example

No specific code snippet can be provided without modifying the existing codebase, but the investigation should focus on adding a TUI-specific branch in gateway/run.py similar to the existing Discord/Telegram handling, and ensuring tui_gateway/ properly handles process completion events.

Notes

The solution may require modifications to both gateway/run.py and tui_gateway/ to achieve parity with Discord/Telegram notification delivery in TUI sessions. The exact changes depend on the current implementation details not fully provided in the issue.

Recommendation

Apply a workaround by using process(action='poll', session_id=...) explicitly until a proper fix can be implemented in the gateway/run.py and tui_gateway/ code to support TUI notifications upon process completion.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - ✅(Solved) Fix TUI: notify_on_complete silently drops — no notification delivered for background terminal processes [1 pull requests, 1 participants]