fix(agents): preserve unapplied Claude model drafts across navigation
This commit is contained in:
1 parent
476d6e6769
commit
7b192cc6a4
2 files changed
+50
-19
No files matched your search
@@ -141,6 +141,10 @@ const createClaudeBooleanByClient = (): Record<ClaudeModelMappingClientId, boole
|
||||
'claude-desktop': false,
|
||||
});
|
||||
|
||||
let claudeModelMappingsDraftCache = createClaudeModelMappingsByClient();
|
||||
let claudeCustomMappingCache = createClaudeBooleanByClient();
|
||||
const claudeModelMappingsDirtyCache = createClaudeBooleanByClient();
|
||||
|
||||
const claudeMappingRoles = [
|
||||
{
|
||||
key: 'opus',
|
||||
@@ -563,11 +567,14 @@ export function AgentsPage() {
|
||||
const [modelByClient, setModelByClient] = useState<Partial<Record<AgentClientId, string>>>(
|
||||
readAgentModelSelections,
|
||||
);
|
||||
const [claudeModelMappingsDraftByClient, setClaudeModelMappingsDraftByClient] = useState(
|
||||
createClaudeModelMappingsByClient,
|
||||
const [claudeModelMappingsDraftByClient, setClaudeModelMappingsDraftByClientState] = useState(
|
||||
() => ({
|
||||
'claude-code': { ...claudeModelMappingsDraftCache['claude-code'] },
|
||||
'claude-desktop': { ...claudeModelMappingsDraftCache['claude-desktop'] },
|
||||
}),
|
||||
);
|
||||
const [claudeCustomMappingByClient, setClaudeCustomMappingByClient] = useState(
|
||||
createClaudeBooleanByClient,
|
||||
const [claudeCustomMappingByClient, setClaudeCustomMappingByClientState] = useState(
|
||||
() => ({ ...claudeCustomMappingCache }),
|
||||
);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [modelLoading, setModelLoading] = useState(false);
|
||||
@@ -589,7 +596,31 @@ export function AgentsPage() {
|
||||
const [piProviderUpdateStatus, setPiProviderUpdateStatus] = useState<PiProviderUpdateStatus | null>(null);
|
||||
const modelRequestRef = useRef(0);
|
||||
const piUpdateRequestRef = useRef(0);
|
||||
const claudeModelMappingsDirtyRef = useRef(createClaudeBooleanByClient());
|
||||
const claudeModelMappingsDirtyRef = useRef(claudeModelMappingsDirtyCache);
|
||||
|
||||
const setClaudeModelMappingsDraftByClient = useCallback((
|
||||
update: (
|
||||
current: Record<ClaudeModelMappingClientId, ClaudeModelMappings>,
|
||||
) => Record<ClaudeModelMappingClientId, ClaudeModelMappings>,
|
||||
) => {
|
||||
setClaudeModelMappingsDraftByClientState((current) => {
|
||||
const next = update(current);
|
||||
claudeModelMappingsDraftCache = next;
|
||||
return next;
|
||||
});
|
||||
}, []);
|
||||
|
||||
const setClaudeCustomMappingByClient = useCallback((
|
||||
update: (
|
||||
current: Record<ClaudeModelMappingClientId, boolean>,
|
||||
) => Record<ClaudeModelMappingClientId, boolean>,
|
||||
) => {
|
||||
setClaudeCustomMappingByClientState((current) => {
|
||||
const next = update(current);
|
||||
claudeCustomMappingCache = next;
|
||||
return next;
|
||||
});
|
||||
}, []);
|
||||
|
||||
const loadStatuses = useCallback(async (forceRefresh = false) => {
|
||||
const command = forceRefresh
|
||||
@@ -685,9 +716,8 @@ export function AgentsPage() {
|
||||
setClearConfirmOpen(false);
|
||||
setOauthLoginRequiredAction(null);
|
||||
setOauthConfigurationDraft(null);
|
||||
if (selected === 'claude-code' || selected === 'claude-desktop') {
|
||||
claudeModelMappingsDirtyRef.current[selected] = false;
|
||||
}
|
||||
// Preserve each Claude client's unsaved draft while navigating between clients.
|
||||
// Its dirty flag is cleared only after a successful apply, close, or reset action.
|
||||
}, [selected]);
|
||||
|
||||
const activeDefinition = agentDefinitions.find((agent) => agent.id === selected)
|
||||
|
||||
@@ -100,7 +100,7 @@ const appliedConfiguration = (
|
||||
});
|
||||
|
||||
describe('agent configuration update action', () => {
|
||||
test('Claude mapping clients do not inherit another client draft', () => {
|
||||
test('Claude mapping clients keep their own unsaved draft after switching away and back', () => {
|
||||
const codeDraft = { opus: 'code-opus', sonnet: 'code-sonnet', haiku: 'code-haiku' };
|
||||
const desktopDraft = {
|
||||
opus: 'desktop-opus',
|
||||
@@ -116,26 +116,27 @@ describe('agent configuration update action', () => {
|
||||
'claude-code': codeDraft,
|
||||
'claude-desktop': desktopDraft,
|
||||
};
|
||||
const dirtyByClient = {
|
||||
'claude-code': true,
|
||||
'claude-desktop': false,
|
||||
};
|
||||
|
||||
// Switch from Claude Code to Desktop: Desktop still loads its own default.
|
||||
expect(resolveAgentModelMappingsDraftSourceForClient(
|
||||
drafts,
|
||||
'claude-desktop',
|
||||
null,
|
||||
desktopFallback,
|
||||
false,
|
||||
dirtyByClient['claude-desktop'],
|
||||
)).toEqual(desktopFallback);
|
||||
expect(resolveAgentModelMappingsDraftSourceForClient(
|
||||
drafts,
|
||||
'claude-desktop',
|
||||
null,
|
||||
desktopFallback,
|
||||
true,
|
||||
)).toEqual(desktopDraft);
|
||||
|
||||
// Switch back to Claude Code: its unapplied selection must not be overwritten.
|
||||
expect(resolveAgentModelMappingsDraftSourceForClient(
|
||||
drafts,
|
||||
'claude-code',
|
||||
null,
|
||||
{ opus: 'applied', sonnet: 'applied', haiku: 'applied' },
|
||||
desktopFallback,
|
||||
true,
|
||||
dirtyByClient['claude-code'],
|
||||
)).toEqual(codeDraft);
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user