Skip to content

fix(agent): don't attach a pre-API steer to a prior turn's tool result (#107272) - #107284

Closed
PRATHAMESH75 wants to merge 1 commit into
NousResearch:mainfrom
PRATHAMESH75:fix/steer-preapi-stale-tool-injection
Closed

PRATHAMESH75 wants to merge 1 commit into
NousResearch:mainfrom
PRATHAMESH75:fix/steer-preapi-stale-tool-injection

Conversation

@PRATHAMESH75

Copy link
Copy Markdown

What

In the classic CLI, a /steer typed while an attached image is still being preprocessed — before the turn's first API call — is acknowledged ("⏩ Steer queued — arrives after the next tool call") but the turn completes without applying it, and nothing indicates it was dropped (#107272).

Root cause

The steer is accepted into _pending_steer, then drained pre-API in turn_iteration_prep and handed to _inject_steer_after_newest_tool_result, which walks the message list backward and inserts the steer after the newest role == "tool" message. That helper is written for mid-turn steering (between tool-call iterations), where the newest tool result belongs to the current turn.

On a turn's first iteration, there is no current-turn tool result yet. If the session already contains a prior turn's tool result (the issue's repro: "a session that already contains at least one tool result"), that stale tool message sits before the current user prompt. The steer is therefore inserted into already-persisted history, ahead of the current turn — exactly the "attached to an earlier turn" case the issue calls out — and the model ignores it as old context.

Fix

_inject_steer_after_newest_tool_result now stops the backward scan at the current user row. A tool message only counts as an injection target when it's newer than the last user row (i.e. part of the current turn). Otherwise the steer is re-queued via _requeue_pending_steer, so:

  • the turn finalizer captures it into result["pending_steer"] (existing path), and
  • the CLI delivers it as the next turn with ⏩ Delivering leftover /steer as next turn (existing path, cli_chat_turn_mixin.py) — visible, correctly ordered, not lost.

This matches the issue's Expected Behavior ("queue it without attaching it to an earlier turn"). Mid-turn steering is unchanged: there the current-turn tool result is the newest message, so it's found before any user row.

Tests

tests/run_agent/test_steer.py::TestPreApiCallSteerDrain:

  • test_pre_api_drain_restashes_when_newest_tool_is_from_a_prior_turn — first-iteration steer with a prior-turn tool result before the current user prompt: history is untouched and the steer is re-queued into _pending_steer.
  • Existing test_pre_api_drain_appends_user_row_and_leaves_tool_row_untouched (mid-turn) and test_pre_api_drain_restashes_when_no_tool_message still pass.

Full file: 38 passed. ruff check clean.

Fixes #107272

NousResearch#107272)

A /steer accepted on a turn's first iteration — e.g. typed while an attached image is
still preprocessing, before the first API call — was drained pre-API and injected after
the newest `role=="tool"` message anywhere in the list. When the session already holds a
prior-turn tool result, that message sits BEFORE the current user prompt, so the steer
landed in already-persisted history ahead of the current turn and was silently ignored:
the CLI acknowledged "arrives after the next tool call" but the turn never applied it.

`_inject_steer_after_newest_tool_result` now stops the backward scan at the current user
row: the newest tool message only counts when it belongs to the current turn (after the
last user row). Otherwise the steer is re-queued, so the turn finalizer surfaces it as the
next turn (the CLI already delivers `result["pending_steer"]` as the next prompt) instead
of losing it — matching the issue's expected behavior. Mid-turn steering between tool
iterations is unchanged, since there the current-turn tool result is the newest message.
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state duplicate This issue or pull request already exists labels Sep 10, 2026
@alt-glitch

Copy link
Copy Markdown

This was generated by AI during triage.

Duplicate of #107274, which landed first and fixes the same _inject_steer_after_newest_tool_result scan in agent/turn_iteration_prep.py with the same mechanism (stop at the current turn's user row, re-queue the steer). Both address #107272. Leaving both open for the maintainer to pick; #107274 is canonical as the earlier submission.

@PRATHAMESH75

Copy link
Copy Markdown
Author

Closing in favour of #107274 by @KoNit-K, opened ~7 min earlier with an equivalent
fix for #107272. Both stop a pre-API steer from attaching to a prior turn's tool
result; #107274 threads the current-turn user index explicitly from the caller,
this PR inferred the same floor by scanning back to the current user row. Same
behaviour — deferring to the earlier one. Thanks @KoNit-K.

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 duplicate This issue or pull request already exists P2 Medium — degraded but workaround exists 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.

[Bug]: /steer sent during image preprocessing is acknowledged but ignored

2 participants