fix(ui): stop pinning inherited effort as a session choice on send

Sending recorded the effective variant - inherited settings/agent
defaults included - as the session's explicit per-session choice, so the
picker jumped from Default to the inherited effort right after the send
and the session stopped following later default changes. Record only an
explicit picker override when the send reflects the live selection;
sends carrying a captured configuration for another session keep their
captured variant.
This commit is contained in:
Iuliia Ivashko
2026-09-04 02:16:21 +03:00
parent bbb97f7e66
commit 0a47b306d6
2 changed files with 110 additions and 1 deletions
@@ -4,6 +4,7 @@ import { useProjectsStore } from '@/stores/useProjectsStore';
import { useDirectoryStore } from '@/stores/useDirectoryStore';
import { useSessionWorktreeStore } from './session-worktree-store';
import { expandSlashCommandGoalObjective, routeMessage, useSessionUIStore } from './session-ui-store';
import { useSelectionStore } from './selection-store';
import { setActionRefs, setOptimisticRefs } from './session-actions';
import { useSkillsStore } from '@/stores/useSkillsStore';
import { useCommandsStore } from '@/stores/useCommandsStore';
@@ -352,6 +353,100 @@ describe('sendMessage captured target', () => {
});
});
describe('sendMessage effort recording', () => {
let originalSendMessage;
const calls = [];
beforeEach(() => {
calls.length = 0;
const childStore = {
getState: () => ({ session: [], message: {}, part: {}, session_status: {} }),
setState: () => {},
};
const childStores = {
children: new Map(),
ensureChild: () => childStore,
getChild: () => childStore,
};
setActionRefs(opencodeClient, childStores, () => '/current/project');
setOptimisticRefs(() => {}, () => {});
useConfigStore.setState({ isConnected: true });
useSessionUIStore.setState({
currentSessionId: 'session-current',
currentSessionDirectory: '/current/project',
newSessionDraft: { open: false, directoryOverride: null, parentID: null },
});
originalSendMessage = opencodeClient.sendMessage;
opencodeClient.sendMessage = async (params) => {
calls.push(params);
return 'msg';
};
});
afterEach(() => {
opencodeClient.sendMessage = originalSendMessage;
});
const sendWithVariant = (variant, sessionId) => useSessionUIStore.getState().sendMessage(
'hello',
'provider-a',
'model-a',
'plan',
undefined,
undefined,
undefined,
variant,
'normal',
{ target: { runtimeKey: getRuntimeKey(), sessionId, directory: '/current/project' } },
);
test('does not pin an inherited effort as the session choice', async () => {
useConfigStore.setState({
currentProviderId: 'provider-a',
currentModelId: 'model-a',
currentAgentName: 'plan',
currentVariant: 'low',
currentVariantSelection: { override: undefined, inherited: 'low' },
});
await sendWithVariant('low', 'session-current');
// The send still carries the inherited effort; it is just not recorded
// as this session's explicit choice.
expect(calls[0].variant).toBe('low');
expect(useSelectionStore.getState().getAgentModelVariantForSession('session-current', 'plan', 'provider-a', 'model-a')).toBe(undefined);
});
test('records an explicit effort choice with the send', async () => {
useConfigStore.setState({
currentProviderId: 'provider-a',
currentModelId: 'model-a',
currentAgentName: 'plan',
currentVariant: 'high',
currentVariantSelection: { override: 'high', inherited: 'low' },
});
await sendWithVariant('high', 'session-current');
expect(useSelectionStore.getState().getAgentModelVariantForSession('session-current', 'plan', 'provider-a', 'model-a')).toBe('high');
});
test('keeps the captured variant for a send targeting another session', async () => {
useConfigStore.setState({
currentProviderId: 'provider-a',
currentModelId: 'model-a',
currentAgentName: 'plan',
currentVariant: 'low',
currentVariantSelection: { override: undefined, inherited: 'low' },
});
await sendWithVariant('low', 'session-other');
expect(useSelectionStore.getState().getAgentModelVariantForSession('session-other', 'plan', 'provider-a', 'model-a')).toBe('low');
});
});
describe('slash-command goal objectives', () => {
test('expands every $ARGUMENTS reference from the authoritative command template', () => {
expect(expandSlashCommandGoalObjective('/issue--to-pr LIN-123 --draft', [{
+15 -1
View File
@@ -1707,7 +1707,21 @@ export const useSessionUIStore = create<SessionUIState>()((set, get) => ({
if (targetSessionId && effectiveAgent) {
useSelectionStore.getState().saveSessionAgentSelection(targetSessionId, effectiveAgent)
useSelectionStore.getState().saveAgentModelForSession(targetSessionId, effectiveAgent, providerID, modelID)
useSelectionStore.getState().saveAgentModelVariantForSession(targetSessionId, effectiveAgent, providerID, modelID, variant)
// `variant` is the effective effort, inherited defaults included.
// Recording it verbatim would pin the inherited default as this
// session's explicit choice, so the picker jumps from "Default" to
// that effort after the send. When the send reflects the live picker
// selection, record only an explicit override; sends carrying a
// captured configuration for another context keep their variant.
const configState = useConfigStore.getState()
const reflectsLiveSelection = targetSessionId === get().currentSessionId
&& configState.currentProviderId === providerID
&& configState.currentModelId === modelID
&& configState.currentAgentName === effectiveAgent
const variantToRecord = reflectsLiveSelection
? configState.currentVariantSelection.override ?? undefined
: variant
useSelectionStore.getState().saveAgentModelVariantForSession(targetSessionId, effectiveAgent, providerID, modelID, variantToRecord)
}
if (targetSessionId) {