fix(tui): store a copy of the selected model, not the caller's object
Issue for this PR
Closes #1364
Type of change
- Bug fix
- New feature
- Refactor / code improvement
- Documentation
What does this PR do?
selectModel() in packages/tui/src/context/local.tsx stored the model object it was given with setModelStore("model", agent, model). A Solid store keeps the first object set at a path by reference, and later sets at that path merge their fields into that same object (updatePath โ mergeStoreNode).
Opening a conversation calls restoreSession(msg.model), passing the last user message's recorded model, an object owned by the sync store. When that is the first model stored for the agent in the launch, the model store and the message record share one object. Opening another conversation then merges its model into the first conversation's record, so returning to the first conversation restores the second one's model.
The fix stores a copy ({ providerID, modelID }), so the model store never holds or writes into an object owned by another store. It's a three-line change.
How did you verify your code works?
-
New test
test/context/model-store-aliasing.test.tsx. It mounts the realLocalProviderandSyncProviderand feeds two conversations' user messages throughmessage.updatedevents. It then callsrestoreSession()with each conversation's recorded model, as the prompt does, and returns to the first. The test asserts the first conversation's record and the selection on return. It fails onmain(the record reads back as the second conversation's model) and passes with this change. -
In the real TUI, from source, with two conversations recorded on different providers:
Step Recorded model Selected in the prompt, mainSelected, this PR Open A Acme Large Acme Large Acme Large Open B Altimate Base Altimate Base Altimate Base Back to A Acme Large Altimate Base Acme Large -
TUI suite: 339 pass.
dialog-scan-gate> "pressing y chooses scan" fails identically onmain. Typecheck and the marker/branding guards are clean.
Screenshots / recordings
Text captures are in the table above; the change has no visual component.
Checklist
- I have tested my changes locally
- I have not included unrelated changes in this PR
๐ค Generated with Claude Code
https://claude.ai/code/session_016x3nbZU5Vb6HnSTqE7vELm
Summary by cubic
Fixes cross-conversation model aliasing so returning to a previously opened conversation restores its own model instead of a later one's.
Bug Fixes
selectModel()now stores a copy of the model rather than the caller's object; Solid stores keep the first object set at a path by reference and merge later sets into it, so the old code wrote later selections into another conversation's recorded message.- Adds a regression test that reproduces the aliasing by restoring two conversations' recorded models in sequence.
Refactors
- Shares the model-selection test harness and fixtures between the aliasing and stale-Zen cycle tests.
Summary by CodeRabbit
- Bug Fixes
- Switching between conversations no longer changes the model information recorded in their messages.