hermes - ✅(Solved) Fix Bug: /goal continuation loop never fires on gateway (Telegram/Discord) — event enqueued after consumer returns [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#28649Fetched 2026-05-20 04:02:53
View on GitHub
Comments
0
Participants
1
Timeline
8
Reactions
0
Author
Participants
Timeline (top)
labeled ×5cross-referenced ×1referenced ×1subscribed ×1

The /goal command sets the goal and runs the first turn successfully, but the auto-continuation loop never fires on gateway platforms (Telegram, Discord, etc.). The continuation event is enqueued into adapter._pending_messages after _run_agent has already checked for and drained pending events, so the continuation sits orphaned until the user sends another message.

All three goals observed in state_meta show turns_used: 0 and last_verdict: null, confirming the continuation loop has never completed a single cycle on this system.

Error Message

This suggests evaluate_after_turn() may not be reached at all — possibly GoalManager(session_id=sid).is_active() returns False when loaded fresh from DB in _post_turn_goal_continuation, or an exception occurs before the evaluate call and is swallowed by the outer try/except (line 6947, which logs at debug level only).

Root Cause

The continuation event is enqueued at the wrong layer — after _run_agent() has already returned and its internal pending-event consumer has checked for and found nothing.

Code path (all in gateway/run.py):

  1. /goal <text> is processed by _handle_goal_command() (line 9791). Goal state is saved to state_meta DB. A kickoff MessageEvent is enqueued via _enqueue_fifo() into adapter._pending_messages[session_key].

  2. The kickoff event triggers _run_agent() (line 14663). The agent runs, produces a response.

  3. Inside _run_agent(), after the agent turn completes, the code at line 16381 dequeues pending events:

    pending_event = _dequeue_pending_event(adapter, session_key)

    At this point, _pending_messages is empty (the kickoff was already consumed). No pending event is found. _run_agent() processes the result and returns.

  4. After _run_agent() returns, back in the outer call site at line 6945:

    await self._post_turn_goal_continuation(
        session_entry=session_entry,
        source=source,
        final_response=_final_text,
    )

    This calls evaluate_after_turn() → judge says "continue" → continuation event is enqueued via _enqueue_fifo() into adapter._pending_messages[session_key].

  5. The code then returns _agent_result (line 6949), the finally block releases the running-agent state (line 6951), and the session goes idle.

  6. The continuation event is now orphaned in _pending_messages — there is no code path that re-enters the processing loop to consume it. It only gets processed when the user sends a new message, which triggers _start_session_processing() in the adapter.

Fix Action

Workaround

Users can manually send any message (e.g., "continue") after each goal turn to trigger processing of the queued continuation. But this defeats the purpose of the autonomous loop.

PR fix notes

PR #28691: fix(gateway): respawn session processing after goal continuation enqueue

Description (problem / solution / changelog)

What does this PR do?

Fix /goal auto-continue loop never firing on gateway platforms (Telegram, Discord).

The continuation event was enqueued into adapter._pending_messages after _run_agent() had already returned and drained pending events. The event sat orphaned until the user sent another message, so the auto-continue loop never ran — turns_used stayed at 0 for all active goals.

Why CLI works but Gateway doesn't: The CLI (cli.py) has a continuous polling loop in _pending_input, so a synthetic enqueued message gets picked up immediately. The Gateway has no equivalent — it relies on platform adapters to trigger processing, and _enqueue_fifo() does not trigger the adapter.

Before the fix: continuation event enqueued → orphaned in _pending_messages → never consumed → /goal loop stops after first turn.

After the fix: continuation event enqueued → popped from _pending_messages → fed to _start_session_processing() → next turn fires immediately.

Related Issue

Fixes #28649

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)

Changes Made

  • gateway/run.py: _post_turn_goal_continuation() — changed return type from None to bool. Returns True when a continuation event was enqueued, False otherwise.
  • gateway/run.py: _handle_message() — after the finally block releases _running_agents state, checks the _goal_continuation_enqueued flag. If True, pops the actual pending event from adapter._pending_messages and feeds it to adapter._start_session_processing(), following the same pattern as _drain_pending_after_session_command in base.py.

How to Test

  1. Unit tests: python -m pytest tests/gateway/test_goal_verdict_send.py tests/gateway/test_goal_status_notice.py -q
  2. Manual (gateway): Set up a Telegram/Discord gateway. Send /goal <multi-step task>. Observe that continuation turns fire automatically after each response instead of requiring a user message.

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: Windows 11

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — or N/A
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A

Verification Log

Environment: Windows 11, Python 3.14.3

Changed files

  • gateway/run.py (modified, +29/-7)

Code Example

pending_event = _dequeue_pending_event(adapter, session_key)

---

await self._post_turn_goal_continuation(
       session_entry=session_entry,
       source=source,
       final_response=_final_text,
   )

---

goal:20260514_... | active | turns_used=0 | last_verdict=null
goal:20260517_... | active | turns_used=0 | last_verdict=null
goal:20260518_... | active | turns_used=0 | last_verdict=null
RAW_BUFFERClick to expand / collapse

Bug Report: /goal auto-continue loop does not work on gateway platforms

Summary

The /goal command sets the goal and runs the first turn successfully, but the auto-continuation loop never fires on gateway platforms (Telegram, Discord, etc.). The continuation event is enqueued into adapter._pending_messages after _run_agent has already checked for and drained pending events, so the continuation sits orphaned until the user sends another message.

All three goals observed in state_meta show turns_used: 0 and last_verdict: null, confirming the continuation loop has never completed a single cycle on this system.

Environment

  • Hermes Agent: v0.14.0 (2026.5.16)
  • Platform: Telegram (gateway mode), also affects Discord
  • Model: Various (grok-4.3 via xai-oauth, glm-5.1 via zai)
  • OS: macOS (Apple Silicon), gateway running via launchd

Reproduction

  1. Start the gateway (hermes gateway run)
  2. Send /goal <any multi-step task> via Telegram or Discord
  3. Observe: the first turn runs and produces a response
  4. Expected: the judge evaluates, and if "continue", a continuation turn fires automatically
  5. Actual: the agent stops after the first turn. No continuation fires. turns_used remains 0.

Root Cause Analysis

The continuation event is enqueued at the wrong layer — after _run_agent() has already returned and its internal pending-event consumer has checked for and found nothing.

Code path (all in gateway/run.py):

  1. /goal <text> is processed by _handle_goal_command() (line 9791). Goal state is saved to state_meta DB. A kickoff MessageEvent is enqueued via _enqueue_fifo() into adapter._pending_messages[session_key].

  2. The kickoff event triggers _run_agent() (line 14663). The agent runs, produces a response.

  3. Inside _run_agent(), after the agent turn completes, the code at line 16381 dequeues pending events:

    pending_event = _dequeue_pending_event(adapter, session_key)

    At this point, _pending_messages is empty (the kickoff was already consumed). No pending event is found. _run_agent() processes the result and returns.

  4. After _run_agent() returns, back in the outer call site at line 6945:

    await self._post_turn_goal_continuation(
        session_entry=session_entry,
        source=source,
        final_response=_final_text,
    )

    This calls evaluate_after_turn() → judge says "continue" → continuation event is enqueued via _enqueue_fifo() into adapter._pending_messages[session_key].

  5. The code then returns _agent_result (line 6949), the finally block releases the running-agent state (line 6951), and the session goes idle.

  6. The continuation event is now orphaned in _pending_messages — there is no code path that re-enters the processing loop to consume it. It only gets processed when the user sends a new message, which triggers _start_session_processing() in the adapter.

Why the CLI Works but the Gateway Does Not

In the CLI (cli.py), _handle_goal_command() puts the goal into self._pending_input (line 8622), which the CLI interactive main loop reads from continuously. The CLI effectively has a built-in re-entry loop. The gateway has no equivalent — it relies on platform adapters to trigger processing, and a synthetic enqueued event does not trigger the adapter.

Additional Finding: turns_used Stays at 0

Three goals observed in the DB, all with turns_used: 0:

goal:20260514_... | active | turns_used=0 | last_verdict=null
goal:20260517_... | active | turns_used=0 | last_verdict=null
goal:20260518_... | active | turns_used=0 | last_verdict=null

This suggests evaluate_after_turn() may not be reached at all — possibly GoalManager(session_id=sid).is_active() returns False when loaded fresh from DB in _post_turn_goal_continuation, or an exception occurs before the evaluate call and is swallowed by the outer try/except (line 6947, which logs at debug level only).

Suggested Fix

After _post_turn_goal_continuation enqueues the continuation event, the gateway should re-enter _run_agent (or equivalent) to process it. Two approaches:

Option A: Move _post_turn_goal_continuation inside _run_agent(), before the pending-event dequeue at line 16381. This way the continuation event is already enqueued when the dequeue runs.

Option B: After _post_turn_goal_continuation returns at line 6945, check if a continuation was enqueued. If so, call _start_session_processing on the adapter to spawn a new processing task — similar to how _drain_pending_after_session_command works in base.py.

Workaround

Users can manually send any message (e.g., "continue") after each goal turn to trigger processing of the queued continuation. But this defeats the purpose of the autonomous loop.

Related Code

  • gateway/run.py: _post_turn_goal_continuation() (line 9981), _enqueue_fifo() (line 2040), dequeue in _run_agent() (line 16381), continuation hook call site (line 6942)
  • hermes_cli/goals.py: GoalManager.evaluate_after_turn() (line 620), judge_goal() (line 371)
  • gateway/platforms/base.py: _start_session_processing() (line 2646), _drain_pending_after_session_command() (line 2728)

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 Bug: /goal continuation loop never fires on gateway (Telegram/Discord) — event enqueued after consumer returns [1 pull requests, 1 participants]