Skip to content

fix(agent): emit guardrail halt explanation to client stream (#30770) - #30808

Closed
Bartok9 wants to merge 1 commit into
NousResearch:mainfrom
Bartok9:fix/30770-guardrail-halt-emit-to-stream
Closed

fix(agent): emit guardrail halt explanation to client stream (#30770)#30808
Bartok9 wants to merge 1 commit into
NousResearch:mainfrom
Bartok9:fix/30770-guardrail-halt-emit-to-stream

Conversation

@Bartok9

@Bartok9 Bartok9 commented May 23, 2026

Copy link
Copy Markdown
Contributor

Summary

When _tool_guardrail_halt_decision fires the turn loop appended the halt
message to messages (conversation history) but never called
_fire_stream_delta, leaving SSE / TUI clients with a silent stream close
indistinguishable from a crash.

Root Cause

# Before (conversation_loop.py ~3432)
if agent._tool_guardrail_halt_decision is not None:
    ...
    final_response = agent._toolguard_controlled_halt_response(decision)
    agent._emit_status(...)
    messages.append({'role': 'assistant', 'content': final_response})  # history only
    break

final_response was written to internal message history but never pushed to
the stream consumers (stream_delta_callback, TTS, gateway SSE).

Fix

Add a single _fire_stream_delta(final_response) call before messages.append:

agent._fire_stream_delta(final_response)   # ← emit to all stream consumers
messages.append({'role': 'assistant', 'content': final_response})

This matches how the normal (non-guardrail) response path delivers text to clients.

Testing

  • Added test_guardrail_halt_emits_final_response_to_stream to
    tests/agent/test_tool_guardrails.py — verifies _fire_stream_delta is
    called with the halt explanation text whenever _tool_guardrail_halt_decision
    is set.
python -m pytest tests/agent/test_tool_guardrails.py -v
# 1 passed

Fixes #30770

@alt-glitch alt-glitch added type/bug Something isn't working comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P1 High — major feature broken, no workaround labels May 23, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Competing fix: PRs #30807 and #30813 also address #30770. This is the cleanest minimal approach (single _fire_stream_delta call). #30813 additionally covers stream flush and interim message adapters with a reusable helper.

…earch#30770)

When _tool_guardrail_halt_decision fires, the conversation loop appended
the halt message to `messages` (so it persisted to history) but never
called `_fire_stream_delta`, leaving SSE / TUI clients with a silent
stream close indistinguishable from a crash.

Fix: call `agent._fire_stream_delta(final_response)` immediately before
the messages.append so every registered stream consumer (display, TTS,
gateway SSE) receives the explanation.

Adds a regression test that verifies _fire_stream_delta is called with
the halt text whenever _tool_guardrail_halt_decision is set.

Fixes NousResearch#30770
@Bartok9
Bartok9 force-pushed the fix/30770-guardrail-halt-emit-to-stream branch from f788491 to 91f84f9 Compare May 27, 2026 20:40
@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the focused fix. An automated hermes-sweeper review found that the #30770 behavior is already fixed on current main by a competing implementation.

Evidence:

  • agent/conversation_loop.py:3974 now handles guardrail_halt by generating the halt explanation and emitting it through stream_delta_callback(final_response) before flushing the stream, so SSE/TUI clients receive text instead of a silent finish.
  • tests/run_agent/test_tool_call_guardrail_runtime.py:309 adds a regression test for guardrail_halt exits silently — no final assistant message delivered to client #30770 asserting the halt explanation reaches the stream delta callback.
  • Implementation commit: 38b8d0da85aa663e4dd511eea6e084a4d6cf3697 (fix: emit guardrail halt message to client before closing stream).
  • Regression-test commit: 186bf25cb11077b8c158dbfc1f768e48bc28b0db.
  • The fix is contained in v2026.5.28 and later release tags.

The exact helper shape from this PR differs from the implementation that landed, but the reported client-stream failure is covered on main now.

@teknium1 teknium1 closed this Jun 21, 2026
@teknium1 teknium1 added the sweeper:implemented-on-main Sweeper: behavior already present on current main label Jun 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P1 High — major feature broken, no workaround sweeper:implemented-on-main Sweeper: behavior already present on current main type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

guardrail_halt exits silently — no final assistant message delivered to client

3 participants