fix(tui): retry session model hydration until it applies - #38941
Open
iceteaSA wants to merge 1 commit into
Open
Conversation
Contributor
|
Thanks for updating your PR! It now meets our contributing guidelines. 👍 |
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.
Issue for this PR
Closes #38940
Type of change
What does this PR do?
Under
OPENCODE_FAST_BOOT, opening a session whose last user message used a non-default model leaves the composer on the default. The session's selection is dropped, and because Anthropic caching keys on the model, the next turn re-uploads the whole context.What was wrong.
packages/tui/src/component/prompt/index.tsx:310-332set thesyncedSessionIDlatch before running the two setters that do the hydration:Both setters can silently no-op on data that has not loaded yet. An empty agent list makes
isPrimaryAgentfalse and skips the whole block;local.model.set()returns early whenisModelValid()finds no provider, because that readssync.data.provider(context/local.tsx:64-67), still empty while providers load. The latch was already set, so the effect never re-entered and the session keptfallbackModel— CLI arg, then config model, thenrecent[0], then provider default.recent[0]is persisted, so you usually land on your previously selected model rather than an obviously wrong one, which is why it reads as intermittent.Normal boot never hits this.
SyncProviderholds the app behindinit.ready(context/helper.tsx:15) andreadyis false while loading unless fast-boot is on (context/sync.tsx:558), so Prompt does not mount until the catalogs are in. Fast-boot removes that gate, which is what exposes the race.The change. Latch after hydration instead of before, and return early while
sync.status === "loading"and the needed catalog entry is still missing. Both reads stay tracked, so the effect re-runs when the data arrives and settles once it applies.Behaviour after load is unchanged: still one-shot per session, so later messages do not re-hydrate and an unsent local model choice is not overwritten.
How did you verify your code works?
packages/tui/test/cli/tui/model-hydration-race.test.tsxmounts the real contexts withskipInitialLoading: trueand resolves/api/agentafter the session's messages are already available.On unfixed source:
At the moment the effect runs, the message is present,
local.agent.list()is[], andsync.data.provideris[]— bail 1.Reverting the source change turns the test red again and restoring it turns it green, so the test observes the fix rather than passing incidentally.
Running the same scenario with
skipInitialLoading: falsepasses on unfixed source. That is how the fast-boot scoping was established — the first draft of this fix assumed the race applied to normal boot as well, and the test disproved it.Gates from
packages/tui:bun test192 pass / 1 skip / 0 fail;bun typecheckexit 0.Not covered. This does not address the other reported ways a session reverts to the default model — #38165, #38770, #28735. Those come through the server-side prompt path, where an injected prompt without an explicit
modelresolves to the agent's configured default (packages/opencode/src/session/prompt.ts:646). #35195 targets that path.Screenshots / recordings
Not a UI change — behaviour only, covered by the test above.
Checklist