Skip to content

fix: deliver leftover /steer as out-of-band user message (#60543)#60555

Open
rainbowgore wants to merge 1 commit into
NousResearch:mainfrom
rainbowgore:fix/steer-leftover-oob-marker
Open

fix: deliver leftover /steer as out-of-band user message (#60543)#60555
rainbowgore wants to merge 1 commit into
NousResearch:mainfrom
rainbowgore:fix/steer-leftover-oob-marker

Conversation

@rainbowgore

@rainbowgore rainbowgore commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Fixes a /steer message being lost / delivered without its trust marker when it arrives after the final assistant turn.

/steer has three delivery paths. Two wrap the user's message in the [OUT-OF-BAND USER MESSAGE] marker so the model treats it as a genuine user instruction rather than tool output / prompt injection (apply_pending_steer_to_tool_results, and the pre-API drain in conversation_loop.py). The third — the "leftover" path, taken when a steer lands after the final assistant turn with no remaining tool batch to drain into — did not. turn_finalizer.py hands the raw steer back as result["pending_steer"], and both consumers delivered it verbatim as the next user turn, so the model saw bare /steer text instead of the marker.

On the gateway path there was a sharper failure: its downstream slash-command discard net drops any pending text starting with /, so a leftover steer whose text begins with / (e.g. steering with /stop the search) was silently discarded. Wrapping it in the marker means it no longer starts with /, so it survives and is delivered.

Approach: add one shared helper next to the existing steer helpers and route both consumers through it, rather than duplicating the wrap at each call site.

Related Issue

Fixes #60543

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

  • agent/agent_runtime_helpers.py — add format_leftover_steer_for_delivery() (format_steer_marker(text).strip(); the marker is the whole standalone message here, so the append-oriented leading newlines are stripped) and export it.
  • cli.py — CLI REPL leftover handler now delivers format_leftover_steer_for_delivery(_leftover_steer) instead of the raw text.
  • gateway/run.py — gateway leftover handler (TUI / Telegram / Slack / WhatsApp) routed through the same helper.
  • tests/run_agent/test_steer_leftover_marker.py — new regression suite (6 cases).

How to Test

  1. In the TUI, start a conversation that triggers parallel tool calls.
  2. Send /steer "..." so it lands after the final tool batch (the leftover window).
  3. Confirm it reaches the model wrapped as [OUT-OF-BAND USER MESSAGE], not as raw /steer text — and that a steer beginning with / is delivered rather than silently dropped.

Automated: tests/run_agent/test_steer_leftover_marker.py covers both delivery paths, the marker contract, and the gateway slash-command net. Each assertion was confirmed to fail against the pre-fix raw passthrough (red → green).

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • 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 — CI Python tests green on Linux (amd64 + arm64); targeted steer/finalizer/gateway suites (52) pass locally on macOS.
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: macOS 26.5 (arm64), Python 3.12

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — new helper is documented via docstring; no user-facing docs affected — or N/A
  • N/A — no config keys added/changed (cli-config.yaml.example)
  • N/A — no architecture/workflow changes (CONTRIBUTING.md / AGENTS.md)
  • I've considered cross-platform impact (Windows, macOS) — change is platform-agnostic (no file I/O, process, or shell surface touched)
  • N/A — no tool behavior/schema changes

Screenshots / Logs

tests/run_agent/test_steer_leftover_marker.py — 6 passed. Full pytest tests/ -q result noted in the checklist above.

…usResearch#60543)

A /steer that lands after the final assistant turn — with no remaining tool
batch to drain into — is returned as result["pending_steer"] and delivered as
the next user turn. Both delivery sites forwarded it as raw text, so the model
received bare /steer text instead of the [OUT-OF-BAND USER MESSAGE] marker the
mid-batch and pre-API drain paths use. On the gateway path, a steer whose text
starts with "/" could additionally be swallowed by the slash-command discard net.

Add format_leftover_steer_for_delivery() beside the existing steer helpers and
route both consumers (cli.py, gateway/run.py) through it.

Tests: tests/run_agent/test_steer_leftover_marker.py (6 cases) cover both paths,
the marker contract, and the gateway slash-command net. 52 passing across the
steer, concurrent-interrupt, tui-gateway-queue and turn_finalizer suites.
@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 comp/cli CLI entry point, hermes_cli/, setup wizard comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages labels Jul 7, 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 comp/cli CLI entry point, hermes_cli/, setup wizard comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages 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.

/steer race condition: steer lost when arriving between tool batch drain and next API call

2 participants