Pin the provider/model to each session and restore it on switch - #321
Merged
Conversation
The active text provider + chat model are one global config setting, so
every session silently followed whatever the operator last picked —
switch from an OpenRouter/glm thread into a local-llama thread and the
OpenRouter model kept serving it.
- executeTurn stamps metadata.llm = { providerId, chatModel } (resolved
from the live config at turn start, not the fallback chain's
substitute) onto the session with its post-turn save.
- The TUI also stamps the open session the moment the operator picks a
model, so a choice made between turns survives switching away.
- switchSession re-applies the target session's stamp when it differs
from the active model, through the LLM panel's own bus actions
(providers_select_chat_model / providers_set_active_text) so config
persistence + provider reload + panel refresh stay in the one place
that owns them. A stamped provider that was removed changes nothing
and says so; sessions without a stamp keep the current model.
- planModelRestore/readSessionLlmStamp are pure and unit-tested;
malformed metadata degrades to "no stamp", never a crash.
This was referenced Sep 2, 2026
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.
Problem
The active text provider and its chat model are a single global setting, so every session silently follows whatever was last picked. Run one project on
openrouter/z-ai/glm-5.2and another on local-llama, switch between their sessions — and both quietly use the same model. A session has no memory of what it was driven with.Change
Each session now records the provider/model it runs on, and switching into a session re-applies it:
executeTurnwritesmetadata.llm = { providerId, chatModel }with the post-turn save, resolved from the live config at turn start (deliberately not the fallback chain's emergency substitute — the session should remember the operator's choice, not the outage). Every origin funnels throughexecuteTurn, so TUI/scheduler/Telegram/HTTP turns all stamp.providers_select_chat_model/providers_set_active_text), so a choice made between turns survives switching away before the next message.switchSessioncompares the target session's stamp against the active config (planModelRestore, a pure function) and, when they differ, re-applies it by emitting the LLM panel's own bus actions — config persistence, provider reload and panel refresh all happen in the code path that already owns them. A transcript line says what was restored.Scope note: model resolution stays global at runtime — the restore happens when you enter a session. A scheduled/background turn on another session still uses the currently active model; per-turn model plumbing through the engine would be a much larger change and is intentionally out of scope here.
Tests
readSessionLlmStamp: well-formed / model-less / malformed metadataplanModelRestore: no stamp, already active, cross-provider select, legacymodelfield, model-less activate, deleted provider