Skip to content

fix(agent): don't end the turn on tool-call block decisions - #46878

Closed
HagegeR wants to merge 1 commit into
NousResearch:mainfrom
HagegeR:pr/session-guardrail-fix
Closed

HagegeR wants to merge 1 commit into
NousResearch:mainfrom
HagegeR:pr/session-guardrail-fix

Conversation

@HagegeR

@HagegeR HagegeR commented Jun 15, 2026

Copy link
Copy Markdown

Repairs tool_guardrails: a block decision should stop that tool call only, but the controller was setting the turn-halt flag for both block and halt. Result: the agent treated an idempotent-block as a hard stop, halting the session on the first blocked read-only call. Fix: should_halt now matches only action == halt. The block action still returns a synthetic try-different-path result, which is the right agent recovery behavior.

…l does not kill the turn

(cherry picked from commit 3d1a9cd6404de03bad7b62fcca30a4137ea3de2f)
@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 Jun 15, 2026
@teknium1 teknium1 added sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jun 21, 2026
@teknium1

teknium1 commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

Thanks for this, and nice clean diff — but after tracing the original feature I'm going to close this, because the behavior you're changing is intentional rather than a bug.

The tool-loop guardrail (added in the original fix(agent): add tool-call loop guardrails change) ships a runtime test — test_config_enabled_hard_stop_run_conversation_returns_controlled_guardrail_halt_without_top_level_error in tests/run_agent/test_tool_call_guardrail_runtime.py — that explicitly asserts a repeated_exact_failure_block sets turn_exit_reason == "guardrail_halt" and ends the turn. So should_halt matching {block, halt} is the documented, tested contract from day one, not an accidental conflation.

The design intent: once a tool call is proven to be a hopeless loop (same call failed N times, or a read-only call returned the identical result N times), the guardrail synthesizes a controlled tool result, emits the "I stopped retrying X" explanation, and ends the turn cleanly. The synthetic result exists to keep message-role alternation valid on the way out — not to keep the loop going. There is no "block, then continue and recover" path in the feature; that's what halt and block both terminate into.

Flipping should_halt to halt-only makes a block decision non-terminal, which breaks that runtime test (and its stream-delta sibling test_guardrail_halt_emits_final_response_through_stream_delta_callback). Those aren't stale — they encode the behavior.

Worth noting this whole path is gated behind tool_loop_guardrails.hard_stop_enabled: true (the default is warn-only), so it only fires for users who opted into hard stops — and a hard-stop user getting a hard stop on a proven loop is the feature working as designed.

If the goal is "let the agent recover from a recoverable loop instead of halting," that's a real idea, but it belongs as a distinct non-terminal action (a redirect) rather than redefining what block means — see #37490, which takes that shape. Happy to see a focused proposal along those lines.

Appreciate the contribution regardless — closing as intended-behavior.

@teknium1 teknium1 closed this Jul 1, 2026
@HagegeR

HagegeR commented Jul 1, 2026

Copy link
Copy Markdown
Author

@teknium1 thanks for the detailed explanation, I have enabled the flag without understanding it fully which led me to believe something was wrong in the code there, thanks again, and if you would have time to look at the #43154 alpine support I made for the gateway, would be really nice for next release

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:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants