fix(tui): render committed run_code rows through the shared Code Mode rules - #4
Merged
Merged
Conversation
… rules A committed assistant row that carries prose alongside its tool call never reaches the streaming card: its body lines come from collect_tool_block_body_lines_with_width, which knew nothing about Code Mode. Every code_mode/dispatch progress event the row had accumulated was drawn as body text, so reopening a session replayed the whole dispatch stream, and the completion summary the streaming card shows was missing. Route `run_code` through the same rules that card uses, reusing the shared rebon_render helpers instead of reimplementing them: the summary from completed_summary, the final output with progress events filtered out by tool_progress_metadata, a single reason line for a failed call (raw_output["error"], else the last content block), and the program under a JavaScript: label in Verbose only, since a committed row has no Ctrl+O expansion to hide it behind. The existing preview budget still bounds non-Verbose bodies, and every other tool's body is unchanged.
The four demo recordings were 1018x694 and 1.0 MB together, which pushed the rest of the Demo section out of sight. Re-encode them to 780x532 and drop every other frame while summing the dropped frame's duration into the one that replaces it, so each clip keeps its exact running time (verified by parsing the ANMF duration headers: 39980/51280/47660/30960 ms before and after). The total falls to 470 KB and the text stays legible at the width GitHub renders. Lay the demos out as a table with the description on the left and the recording on the right, in both READMEs. The Chinese one references the assets one level up from docs/.
First-prompt routing could only pick a model inside the provider in force: the decision had no provider field, and both the main-session and the worker path pinned the resolve to the current provider and asserted it stayed there. ModelRoutingDecision now carries a provider, and routed_target() reads it in one place for both callers. A provider switch has to name a model with it, because a model id belongs to the provider that serves it — the router never asks one provider for another's model. The classifier is offered every usable provider's models, grouped by provider, and the decision is checked against the provider it names: the model has to belong to it, and the reasoning effort has to be one that model supports. routerModel itself still has to belong to the provider in force, since the classification request runs there. A declared provider now suppresses automatic routing the way a declared model already did: a route that can move the worker must not overrule a provider the dispatch named. The routing notice names the provider it switched to, so a session that changes legs says so.
The classifier's system prompt was fixed, and biased toward the cheapest suitable candidate, so routing could never be told that hard work deserves a frontier model. A user could name the classifying model and nothing else. The plugin now takes a `policy` setting: free text, appended to the classifier's prompt and marked as outranking the cost preference, with a text row in the settings panel beside the model row. The row stores the user's words as written; empty removes the key, so "no policy" is one state in the file rather than two that differ by whitespace. The effort check asked the model table whether the target model supports the effort, and refused when the table carried no row for it. Every custom provider's own model id is such a model, so a decision naming an effort on the provider's default model was rejected and the turn ran unrouted. The table speaks only for the models it lists; where it has no row, the provider's own declaration governs.
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.
What
run_code(Code Mode) rows that carry prose next to the tool call never reach thestreaming card: their body comes from
collect_tool_block_body_lines_with_width,which knew nothing about Code Mode. Every accumulated
code_mode/dispatchprogressevent was therefore drawn as body text, so reopening a session replayed the whole
dispatch stream and the completion summary the streaming card shows was missing.
run_codenow goes through the same rules that card uses, reusing the sharedrebon_renderhelpers instead of reimplementing them: the summary fromcompleted_summary, the final output with progress events filtered out bytool_progress_metadata, a single reason line for a failed call (raw_output["error"],else the last content block), and the program under a
JavaScript:label in Verboseonly, since a committed row has no Ctrl+O expansion to hide it behind. A completed call
with no output says
No final output received, matching the streaming card. Theexisting preview budget still bounds the body.
README
The four demo recordings were 1018x694 and 1.0 MB together. They are re-encoded to
780x532, with each dropped frame's duration summed into the frame that replaces it, so
every clip keeps its exact running time (39980/51280/47660/30960 ms before and after,
verified by parsing the ANMF duration headers). The total is now 471 KB. Both READMEs
lay the demos out as a table with the description on the left and the recording on the
right.
Known trade-off: the frame count is halved, so playback drops from about 5.3 fps to
2.6 fps. The timeline is unchanged, but motion is choppier.
Verification
cargo test -p rebon-tui --lib— 1243 passed, 0 failedcargo test -p rebon-render— 671 passed, 0 failedcargo test -p rebon-cli— 1980 + 1 + 23 passed, 0 failedrustfmt --edition 2021 --checkclean on the changed files