backend: switch the agent CLI without restarting - #10
Merged
Merged
Conversation
/backend swaps the subprocess in place. No re-exec: restartResuming exists for launch flags that cannot change any other way, and a backend switch replaces the process anyway, so using it would discard the scrollback to achieve what the running process can already do. Three things make the swap safe. Close runs on its own goroutine. It blocks on Wait, and doing that inline is the same deadlock main tears down after p.Run to avoid. The new engine is piped into the program that is already running, reached through progRef. The program is built from the model, so the model cannot be handed it at construction, and every later model is a value copy — a pointer to a holder survives that, the same trick as model.content. A failed spawn leaves the old engine untouched and running, so a missing CLI costs an error line rather than the session. The engine we switch away from keeps draining into the same program until it exits, so its trailing frames and its EOF are now ignored. Without that the old subprocess announces "session ended" into the session that just started. What does not survive is the conversation. A session id from one backend means nothing to the other, so the new one starts fresh and the divider says so rather than leaving it to be noticed. resetForBackend clears the rest: a model list from the wrong backend offers rows that cannot be selected, a carried-over session id would be filed under the new backend, and ctxLimit returns to the -ctx floor. The launch --resume is stripped too, or switching a resumed session hands one backend an id the other invented.
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.
/backendswaps the subprocess in place, which is the feature this whole line of work was for.No re-exec.
restartResumingexists for launch flags that cannot change any other way; a backend switch replaces the process anyway, so using it would discard the scrollback to achieve what the running process can already do.Three things make the swap safe
Closeruns on its own goroutine. It blocks onWait, and doing that inline is the same deadlockmaintears down afterp.Runto avoid.progRef. The program is built from the model, so the model cannot be handed it at construction, and every later model is a value copy. A pointer to a holder survives that — the same trick asmodel.content.The bug self-review caught
The engine you switch away from keeps draining into the same program until it exits. Its trailing frames, and the EOF that follows, would land in the new session — the EOF in particular printing
— session ended —into a session that had just started. Stale frames are now ignored by backend, and the test for it is discriminating.What does not survive
The conversation. A session id from one backend means nothing to the other, so the new one starts fresh and the divider says so rather than leaving you to notice.
resetForBackendclears the rest, each for a reason: a model list from the wrong backend offers rows that cannot be selected, a carried-over session id would be filed under the new backend, andctxLimitreturns to the-ctxfloor because the window it grew to described a model that is gone. The launch--resumeis stripped fromExtraArgstoo, or switching a resumed session hands one backend an id the other invented.Verified
go vet, full suite. Every declined path is tested for saying why and for leaving the old engine running — a silent decline reads as "switched" and is not.Not verified: the swap driven through the real TUI. The switch is exercised with a stand-in subprocess, but nobody has pressed
/backendin a terminal.