Skip to content

fix(local): keep a continued reply's space, retire a stale retry note on Stop, fix the out-of-memory notice - #410

Merged
Broccolito merged 1 commit into
mainfrom
fix/local-retry-edges
Oct 18, 2026
Merged

Broccolito merged 1 commit into
mainfrom
fix/local-retry-edges

Conversation

@Broccolito

Copy link
Copy Markdown
Collaborator

Summary

Three defects the final regression run found on main after #403-#407, in the local providers' stream retry. Each was reproduced independently before it was fixed.

  1. A continued reply lost the space where it joins the cut-off text.
    • Bug: a reply cut at a word boundary and then continued read "The quick brown foxjumps over the lazy dog." This reproduced with a real GPT-5.5 proxied through a mock llama-server.
    • Cause: recover_leaked_tool_calls (providers/formats/text_tool_calls.rs) trimmed the leading whitespace of the first text of every completion, continuations included.
    • Fix: the agent now marks a provider call that continues the model's own cut-off text (CONTINUES_A_REPLY), and recovery keeps that call's leading whitespace. The mark is read from the shape of the history: the model's text followed by an agent-only user row. That shape also matches a few fresh replies after another agent-only note (self-critique or done-gate feedback, a supervision prompt). Those keep a leading space too, which is harmless. Whitespace alone is still no reply.
  2. Stop during a retry left a "continue the reply" order in front of the next message.
    • Bug: stopping while the retry of a partial reply waited left the retry note agent-visible, and the person's next message went out merged behind it. With GPT-5.5, "test" was answered with " jumps over the lazy dog."
    • Fix: a Stop or failed stream while a retry waits now hides that note from the model in place, with a compare-and-set of its metadata; nothing is deleted. This happens on every surface:
      • the daemon runner and Crew, in settle_interrupted_turn and record_turn_stopped;
      • the CLI and ACP, which call Agent::retire_unanswered_retry_note once the turn's token is cancelled.
    • A note whose retry ran, or one followed by an agent-visible row, stays.
    • The pattern predates this cycle: before fix(agent): keep a retried turn answering the user, not the error #405, every retry left a similar note.
  3. The GPU out-of-memory notice said "try again" right before "Trying again (1/1)…". It now follows fix: post-merge edge cases in stream retry, serve behind a proxy, and script artifacts #407's rule for the compute-failure notice. This wording was already in v1.93.0.

Test plan

  • New tests: text_tool_calls.rs (a continuation keeps its joining whitespace), llamacpp_sidecar.rs (an_out_of_memory_failure_does_not_ask_the_person_to_try_again), and five retire tests in tests/output_recovery_agent_loop.rs (Stop, failed stream, the reply loop's own stop, a client that settles no turn, a client that drops the stream). Every new test fails with its fix undone, checked by mutation.

  • Ran: cargo fmt --check, ./scripts/clippy-lint.sh, cargo test -p biorouter --lib -- providers:: agents::, --test output_recovery_agent_loop, and the steer and stack binaries (soft_interrupt_agent_loop, turn_abort_tests, subagent_delegation, steer_always_lands).

  • Live against a mock llama-server, on the desktop's HTTP path, the CLI and ACP: each reproduction fails with the pre-fix binary and passes with this build.

  • Known and not changed here:

    • a stopped delegated subagent settles through its own path and keeps the note;
    • a steer that lands after the note keeps it visible.

    Both are narrower than the reported Stop path.

🤖 Generated with Claude Code

…red retry note on Stop, and drop 'try again' from the GPU out-of-memory notice

Three defects the final regression run found in the stream retry of
#403/#405/#407, each reproduced independently first.

- A completion that continues a cut-off reply (a retry after a partial
  reply, or a continuation past the output limit) no longer loses its
  leading whitespace in the local providers' stream recovery, so
  'brown fox' + ' jumps' no longer reads 'foxjumps'. The agent marks the
  provider call (CONTINUES_A_REPLY) from the shape of the history: the
  model's own text followed by an agent-only user row. That shape also
  matches a few fresh replies after another agent-only note (self-critique
  or done-gate feedback, a supervision prompt); those keep a leading space
  too, which is harmless. Whitespace alone is still no reply (R14).
- A Stop, or a failed stream, while a provider retry waits hides that
  retry's note from the model in place (a compare-and-set of its metadata;
  nothing is deleted), so the person's next message is not sent behind a
  'continue the reply' order the retry never acted on. The daemon runner
  and Crew do it in settle_interrupted_turn and record_turn_stopped; the CLI
  and ACP call Agent::retire_unanswered_retry_note once the turn's token is
  cancelled, because there a Stop usually ends the stream on its own rather
  than through the cancel arm. A note the retry answered stays, as does a
  note followed by an agent-visible row such as a steer.
- Llama Server's GPU out-of-memory notice, shown right before 'Trying
  again (1/1)', no longer asks the person to try again or says the server
  was restarted, matching the compute-failure notice.
@Broccolito
Broccolito merged commit 0cce9a8 into main Oct 18, 2026
17 checks passed
@Broccolito
Broccolito deleted the fix/local-retry-edges branch October 18, 2026 08:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant