Repository navigation
ReActV2: a plain-text answer step is never a thought; plain-text replay keeps roles alternating - #349
Merged
Merged
Conversation
…ay keeps roles alternating Under finish: :submit, a step recorded under finish: :auto as the turn's plain-text answer (text plus the signature's outputs) was treated as a thought-only step: the unsent line followed each delivered answer, and models re-sent them. A step that carries the outputs is an answer: no line, never moved to the user side. In a plain-text loop, a submit-era thought-only step followed by its submit step replayed as two assistant messages in a row; they are now one message, the thought then the answer, as a step that wrote both renders.
…instead of joining it to the answer Joining a thought-only step to the answer that followed presented an unsent thought as part of a sent answer. A plain-text loop now follows each thought-only step with a user message, "Your message above was not sent." (no submit to name there), so roles alternate and nothing is merged. A history written only in a plain-text loop has no thought-only step and replays byte-for-byte as before; the existing rejected-submit test is restored as it was.
deepfates
enabled auto-merge (squash)
October 16, 2026 08:06
deepfates
added a commit
that referenced
this pull request
Oct 16, 2026
Version 0.14.1 with tag links for v0.14.1; install lines stay ~> 0.14.0, as for earlier patch releases. The changelog's Unreleased section goes under 0.14.1, and the release notes describe #349. A patch: #349 adds no option and no public API, and fixes how ReActV2 replays histories that cross 0.14.0's opt-in finish: :submit and the default :auto. A history written only in a plain-text loop replays byte-for-byte as in 0.14.0.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
User story
A host changes a program's ending mid-conversation, from
finish: :autotofinish: :submit, and passes the same history on.Under
finish: :auto, a step that answered in plain text is recorded with its text and the signature's outputs. In 0.14.0 the Chat adapter took that step for a thought-only step underfinish: :submit. So "Your message above was not sent. To reply, callsubmitwith …" followed every answer that had in fact been delivered. Models then sent those answers again. In one rehearsal a model re-filed an answer it said it had "composed yesterday and never cleared the send queue".Change
An answer is never a thought.
thought_as_requestnever moves it to the user side.submitstep's answer is its call, so it was already excluded.The reverse case: a
submit-era history replayed in a plain-text loop. A thought-only step used to render as a bare assistant message, often right before the answer's. That is two assistant messages in a row, and in a plain-text loop it reads as a message that was sent.submitinstruction is left out because a plain-text loop has nosubmit, so naming it would be false.finish: :auto.Plain-text-only histories are unchanged. A history written only in a plain-text loop has no thought-only step: its text steps carry the answer. So its requests are byte-for-byte what 0.14.0 sent.
origin/main. Both SHA-256 hashes match.Still two assistant messages in a row, outside this rule: one existing case still replays that way under
:auto. A step whose only call was asubmitthe loop rejected loses the call and its error result on replay, so its text appears bare. That step had a call, so it is not a thought-only step under the definition here. Its existing test (react_v2_test.exs, "a rejected or id-less recorded submit…") is restored unchanged.Known gap
Under
finish: :submit, consider a forced request with nothing new to ask (no inputs, no note) on a history whose last step is a plain-text answer. It still ends on that assistant message, because an answer is not moved. Resuming with new inputs or a note ends on a user turn. This is stated in react.md section 3 and in the CHANGELOG.Tests
test/react_v2_ending_switch_test.exs. The first two fail on main; the third passes on main and here by design:finish: :auto, replayed underfinish: :submit: user, answer, user, answer, user. There is no line.submit-era thought plus answer, replayed underfinish: :auto: thought, "Your message above was not sent.", answer, then the new ticket.There are CHANGELOG entries under Fixed, including the known gap.
mix checkandmix quality.checkpass.