fix(gateway): label goal continuations and avoid silent queued turns - #58039
fix(gateway): label goal continuations and avoid silent queued turns#58039lkz-de wants to merge 2 commits into
Conversation
|
Confirmed on Telegram (v0.18.0). When the goal loop stalls there is no indication. Even when continuations do enqueue, the synthetic message looks like a normal reply. The explicit marker and queue notice here would help surface the problem. |
teknium1
left a comment
There was a problem hiding this comment.
Thanks for isolating the continuation-labeling and interrupted-turn UX issues.
Problems
- The new depth-cap notice can be false. Current
gateway/run.py:20076-20087only retains a fullpending_event; a plainpendinginterrupt string callsadapter.queue_message(), but a repository-wide search found no adapter implementation. The added fallback notice can therefore say “Queued for the next turn” after that string was dropped. The new test intests/gateway/test_gateway_silence_tokens.pystubs_run_agent, so it does not execute this branch.
Suggested changes
- Requeue plain
pendingtext as aMessageEventthrough_enqueue_fifobefore returning the notice, and add a depth-cap regression test for an adapter with_pending_messagesand noqueue_message. The focused open PR #61027 implements this retained-event/plain-text distinction.
The shared continuation marker and updated docs are otherwise aligned with the current goal templates in hermes_cli/goals.py:71-108 and cleanup predicate in gateway/run.py:4495-4503.
Automated hermes-sweeper review.
| @@ -19025,7 +19048,21 @@ def _stream_confirmed_final_delivery( | |||
| merge_pending_message_event(adapter._pending_messages, session_key, pending_event) | |||
| elif adapter and hasattr(adapter, 'queue_message'): | |||
| adapter.queue_message(session_key, pending) | |||
There was a problem hiding this comment.
This notice can be false for the plain pending fallback: the unchanged branch above calls adapter.queue_message(session_key, pending), but normal adapters only expose _pending_messages, so no event is stored and _queue_depth() can be zero. Convert plain pending text to a MessageEvent and enqueue it via _enqueue_fifo before returning this notice.
|
@Victornovikov thanks — that confirmation is useful. That matches the problem this PR is trying to surface more clearly: when the goal loop stalls, the active chat can end up with no visible indication, and when a continuation does enqueue, the synthetic follow-up can still look like a normal reply instead of autonomous standing-goal work. This PR tackles those two user-facing failure modes directly by making the continuation marker explicit and by returning a visible queue notice instead of a silent empty turn. One caveat before asking for maintainer review: the automated review above is right that the depth-cap fallback notice can currently claim a message was queued when a plain |
e15a2c5 to
2fdd708
Compare
|
Refreshed this branch onto current Local verification:
|
7cf5e69 to
eb68c3f
Compare
|
Rebased this onto current Follow-up fix: the interrupt depth-cap fallback now only reports Verification across the four touched test files:
|
eb68c3f to
4fd19e1
Compare
|
Rebased this onto current The only manual conflict was an append/append test collision in Verification after the rebase: scripts/run_tests.sh tests/gateway/test_gateway_silence_tokens.py tests/gateway/test_goal_continuation_drain.py tests/gateway/test_goal_status_notice.py tests/cli/test_cli_goal_interrupt.py -- --tb=short -qResult: |
Summary
This PR makes autonomous
/goalfollow-up turns clearer to users and avoids a silent edge case when interrupted turns already have queued follow-ups pending.Problem
Two user-facing problems combined to create a misleading failure mode in chat platforms:
/goalcontinuation turns were injected with the generic marker[Continuing toward your standing goal], which was easy to confuse with a normal reply.That combination made unrelated autonomous follow-up activity in other chats look like a wrong-chat delivery, even when the underlying session/chat routing was correct.
What changed
hermes_cli.goals.CONTINUATION_MARKER[Automated goal continuation — Hermes is pursuing your standing goal, not a message you sent]GatewayRunner._is_goal_continuation_event()to detect synthetic continuation turns via the shared marker instead of a duplicated hardcoded stringGatewayRunner._queue_notice()so queued-turn notices are formatted consistently/goaldocs to match the new continuation labelWhy this is safe
This does not change which chat a message is routed to.
It only:
/goal pause//goal clearcleanup aligned with the current continuation templates via one shared markerTests
Ran:
uv run --extra dev pytest -q tests/gateway/test_gateway_silence_tokens.py tests/gateway/test_goal_status_notice.py tests/cli/test_cli_goal_interrupt.py -k 'goal or queue or silence or interrupt'Result:
18 passedOverlap check
Checked before proposing this PR:
fix(goals): fire goal judge after streamed turns (Ralph loop stuck at turns_used=0)) — that PR fixes judge execution for streamed/goalturns, while this PR focuses on continuation labeling and queue-notice behavior after interruptiongh search issues '"standing goal" continuation' --repo NousResearch/hermes-agentmain/ merged history:/goaland queue-related work exists upstream, but no current-main change found that already adds this explicit continuation marker or this queue-notice-on-silent-interrupt behaviorNotes
The motivating report initially looked like a wrong-chat routing bug on Signal. After tracing logs/session state, the better explanation was:
So this PR intentionally fixes the user-visible ambiguity and silence without overclaiming a routing-layer fix.