Three diagnostics-and-defaults fixes in the stream and provider path - #780
Merged
Conversation
added 2 commits
August 14, 2026 19:37
The tool-call gap timeout reported the residual budget its last wait happened to get, not how long the provider was actually silent. When the gap window drained between iterations — an ordinary slow consumer, since the generator sits parked at its yield while the clock runs — the wait got the 1ms clamp and the message read "timed out after 0s ... the harness narrows to 60s" in a single sentence. as_secs truncating made even the normal case read 59s for a full 60s window. It now reports the measured stall, rounded. The gap budget also moves onto the runtime clock, which is the one that actually spends it; two clocks measuring one window can only disagree. The wire dump's reasoning flag was additional_params.is_some(), which is true for a tool_choice gate or a metadata map with thinking off, so turns got labelled reasoning-enabled while sending no reasoning params. The params and the flag are now the same decision. DeepSeek, GLM, Cerebras, OpenCode and Kimi each restated their default endpoint as an unwrap_or at client construction, duplicating the resolver's table. Four of the five were unreachable, so they were free to drift; and had the resolver ever stopped answering, the stale copy would have become the live endpoint silently. One table now, and a missing endpoint fails the build naming the provider instead of defaulting to rig's assumption — for the OpenAI-compatible clients, the wrong host holding the wrong key. Closes dirge-vpma.24, dirge-vpma.26, dirge-vpma.27
…r twice Reported live: the final answer rendered twice with the dim "repaired 0 input(s): ; 1 invalid" line between the copies. It looks like the old scavenged-tool-call duplicate but is unrelated — it is a render bug, and the repair line is what triggers it. Renderer::stream is handed the WHOLE accumulated response every time and replaces the open block at the buffer tail, but only while `streaming`. write_line seals that block, and the repair-stats summary is emitted once per run immediately before AgentEnd — only when a repair fired or an input was invalid, which is why the duplicate tracks the "N invalid" line. handle_done then re-streams the response to commit it as one reflowable markdown block, takes the sealed branch, and pushes a second block holding the whole answer. The renderer now remembers the committed part of the current logical stream and renders only the remainder, so a re-stream after a seal is a no-op and text arriving after one appends. end_stream() seals and clears that, wired where the response buffer is already cleared: done, tool_call, interjected, error, context_overflow, the reasoning marker and the notice path. Without it a turn repeating the previous answer verbatim would render as nothing — which is the exact case this was reported from, so there is a test for it. Also from the review of #779: land the vpma.22 regression test (the one fix that shipped without its discriminating test — verified here by mutation, red with the fix reverted), correct the LoopConfig api_key / get_api_key docs, which still advertise per-request OAuth resolution that vpma.25 removed, and drop a redundant rebind in rig_stream. Closes dirge-fw0p
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.
The tool-call gap timeout reported the residual budget its last wait
happened to get, not how long the provider was actually silent. When the
gap window drained between iterations — an ordinary slow consumer, since
the generator sits parked at its yield while the clock runs — the wait
got the 1ms clamp and the message read "timed out after 0s ... the
harness narrows to 60s" in a single sentence. as_secs truncating made
even the normal case read 59s for a full 60s window. It now reports the
measured stall, rounded. The gap budget also moves onto the runtime
clock, which is the one that actually spends it; two clocks measuring
one window can only disagree.
The wire dump's reasoning flag was additional_params.is_some(), which is
true for a tool_choice gate or a metadata map with thinking off, so
turns got labelled reasoning-enabled while sending no reasoning params.
The params and the flag are now the same decision.
DeepSeek, GLM, Cerebras, OpenCode and Kimi each restated their default
endpoint as an unwrap_or at client construction, duplicating the
resolver's table. Four of the five were unreachable, so they were free
to drift; and had the resolver ever stopped answering, the stale copy
would have become the live endpoint silently. One table now, and a
missing endpoint fails the build naming the provider instead of
defaulting to rig's assumption — for the OpenAI-compatible clients, the
wrong host holding the wrong key.
Closes dirge-vpma.24, dirge-vpma.26, dirge-vpma.27