Skip to content

Commit fcae817

Browse files
t3dotggclaude
andcommitted
fix(web): replace stale draft model options when carrying a complete selection
Review: setModelSelection merges — a selection without options keeps the draft's existing options, so carrying a thread whose model has no options (default effort/context window) left the resurrected draft's stale option state in place. Add a replaceOptions flag for callers passing a complete snapshot and use it for the working-mode carry. Co-Authored-By: Claude Fable 5 <[email protected]>
1 parent 55a7695 commit fcae817

2 files changed

Lines changed: 24 additions & 6 deletions

File tree

‎apps/web/src/composerDraftStore.ts‎

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,15 @@ interface ComposerDraftStoreState {
406406
setModelSelection: (
407407
threadRef: ComposerThreadTarget,
408408
modelSelection: ModelSelection | null | undefined,
409+
opts?: {
410+
/**
411+
* Replace the stored entry outright instead of preserving its
412+
* existing options when the incoming selection has none. Used when
413+
* the selection is a complete snapshot (e.g. carried from another
414+
* thread) rather than a model-only change.
415+
*/
416+
replaceOptions?: boolean;
417+
},
409418
) => void;
410419
/** Replace the model options for one or more providers in the draft. */
411420
setModelOptions: (
@@ -2594,7 +2603,7 @@ const composerDraftStore = create<ComposerDraftStoreState>()(
25942603
return { draftsByThreadKey: nextDraftsByThreadKey };
25952604
});
25962605
},
2597-
setModelSelection: (threadRef, modelSelection) => {
2606+
setModelSelection: (threadRef, modelSelection, opts) => {
25982607
const threadKey = resolveComposerDraftKey(get(), threadRef) ?? "";
25992608
if (threadKey.length === 0) {
26002609
return;
@@ -2609,8 +2618,10 @@ const composerDraftStore = create<ComposerDraftStoreState>()(
26092618
const nextMap = { ...base.modelSelectionByProvider };
26102619
if (normalized) {
26112620
const current = nextMap[normalized.instanceId];
2612-
if (normalized.options !== undefined) {
2613-
// Explicit options provided → use them
2621+
if (normalized.options !== undefined || opts?.replaceOptions) {
2622+
// Explicit options provided (or the caller passed a complete
2623+
// snapshot whose absent options mean "no options") → use the
2624+
// selection as-is.
26142625
nextMap[normalized.instanceId] = normalized as ModelSelection;
26152626
} else {
26162627
// No options in selection → preserve existing options, update provider+model

‎apps/web/src/hooks/useHandleNewThread.ts‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,12 @@ export function useNewThreadHandler() {
172172
...(carryInteractionMode ? { interactionMode: carryInteractionMode } : {}),
173173
});
174174
if (carryModelSelection) {
175-
setModelSelection(reusableStoredDraftThread.draftId, carryModelSelection);
175+
// The carried selection is a complete snapshot of the viewed
176+
// thread's model state: absent options mean "no options", not
177+
// "keep the stale draft's options".
178+
setModelSelection(reusableStoredDraftThread.draftId, carryModelSelection, {
179+
replaceOptions: true,
180+
});
176181
}
177182
}
178183
setLogicalProjectDraftThreadId(
@@ -253,8 +258,10 @@ export function useNewThreadHandler() {
253258
if (carryModelSelection) {
254259
// After sticky state so the viewed thread's exact selection
255260
// (model + options like effort and context window) wins over the
256-
// globally sticky one.
257-
setModelSelection(draftId, carryModelSelection);
261+
// globally sticky one. replaceOptions: the carried selection is a
262+
// complete snapshot — absent options mean "no options", not "keep
263+
// whatever sticky state just wrote".
264+
setModelSelection(draftId, carryModelSelection, { replaceOptions: true });
258265
}
259266

260267
await router.navigate({

0 commit comments

Comments
 (0)