fix(agents): isolate Claude client model mapping drafts

This commit is contained in:
lzt404 committed 2026-08-11 14:04:46 +08:00
1 parent 927f4de98a
commit 4484e3a190
8 files changed
+62 -19

No files matched your search

+1 -1
View File
@@ -146,7 +146,7 @@ const CLAUDE_DESKTOP_EXTENDED_CONTEXT_WINDOW: u64 = 1_000_000;
const CLAUDE_CODE_MAX_CONTEXT_TOKENS_ENV: &str = "CLAUDE_CODE_MAX_CONTEXT_TOKENS";
const CLAUDE_AUTOCOMPACT_PCT_OVERRIDE_ENV: &str = "CLAUDE_AUTOCOMPACT_PCT_OVERRIDE";
const DISABLE_AUTO_COMPACT_ENV: &str = "DISABLE_AUTO_COMPACT";
const DEFAULT_CLAUDE_AUTO_COMPACT_PCT: u8 = 100;
const DEFAULT_CLAUDE_AUTO_COMPACT_PCT: u8 = 90;
const MODEL_ALIAS_CONFIG_SECTIONS: &[&str] = &[
"codex-api-key",
"openai-compatibility",
+3 -3
View File
@@ -23,7 +23,7 @@ fn claude_agent_config_preserves_existing_fields() {
assert_eq!(value["env"]["ANTHROPIC_AUTH_TOKEN"], DEFAULT_API_KEY);
assert_eq!(value["env"]["ANTHROPIC_MODEL"], "claude-test");
assert_eq!(value["env"][CLAUDE_CODE_MAX_CONTEXT_TOKENS_ENV], "200000");
assert_eq!(value["env"][CLAUDE_AUTOCOMPACT_PCT_OVERRIDE_ENV], "100");
assert_eq!(value["env"][CLAUDE_AUTOCOMPACT_PCT_OVERRIDE_ENV], "90");
assert!(value["env"].get(DISABLE_AUTO_COMPACT_ENV).is_none());
assert!(value["env"].get("CLAUDE_CODE_EFFORT_LEVEL").is_none());
assert_eq!(value["env"]["CLAUDE_CODE_SUBAGENT_MODEL"], "claude-test");
@@ -66,7 +66,7 @@ fn claude_code_inspection_normalizes_legacy_1m_suffix() {
assert!(mappings.sonnet_1m);
assert!(!mappings.haiku_1m);
assert_eq!(mappings.max_context_tokens, 1_000_000);
assert_eq!(mappings.auto_compact_pct, 100);
assert_eq!(mappings.auto_compact_pct, 90);
assert!(!mappings.disable_auto_compact);
fs::remove_dir_all(directory).unwrap();
}
@@ -80,7 +80,7 @@ fn claude_mapping_legacy_json_defaults_1m_preferences_off() {
assert!(!mappings.sonnet_1m);
assert!(!mappings.haiku_1m);
assert_eq!(mappings.max_context_tokens, 200_000);
assert_eq!(mappings.auto_compact_pct, 100);
assert_eq!(mappings.auto_compact_pct, 90);
assert!(!mappings.disable_auto_compact);
}
+1 -1
View File
@@ -576,7 +576,7 @@ export const jaOverrides = {
'agents.claudeCodeRuntime.maxContextTokensHint': 'Claude Code の既定値は 200000 です。上流モデルの上限を超えないでください。',
'agents.claudeCodeRuntime.context1mHint': 'このマッピングで [1m] モデルを使用し、最大コンテキストを 1000000 にします。3 つすべてを無効にすると 200000 に戻ります。',
'agents.claudeCodeRuntime.autoCompactPct': '自動圧縮の開始率',
'agents.claudeCodeRuntime.autoCompactPctHint': '既定値は 100% です。小さい値ほど早く圧縮します。',
'agents.claudeCodeRuntime.autoCompactPctHint': '既定値は 90% です。小さい値ほど早く圧縮します。',
'agents.claudeCodeRuntime.disableAutoCompact': '自動圧縮を無効化',
'agents.claudeCodeRuntime.disableAutoCompactHint': '有効時は /compact による手動圧縮のみ利用できます。',
'agents.claudeDesktopMapping.customMapping': 'モデルエイリアスを使用',
+1 -1
View File
@@ -571,7 +571,7 @@ export const en: Record<MessageKey, string> = {
'agents.claudeCodeRuntime.maxContextTokensHint': 'Claude Code defaults to 200000. Do not exceed the upstream model limit.',
'agents.claudeCodeRuntime.context1mHint': 'Uses the [1m] model for this mapping and sets the maximum context to 1000000. It returns to 200000 after all three mappings are off.',
'agents.claudeCodeRuntime.autoCompactPct': 'Auto-compact trigger',
'agents.claudeCodeRuntime.autoCompactPctHint': 'Defaults to 100%. Lower values compact earlier.',
'agents.claudeCodeRuntime.autoCompactPctHint': 'Defaults to 90%. Lower values compact earlier.',
'agents.claudeCodeRuntime.disableAutoCompact': 'Disable auto-compaction',
'agents.claudeCodeRuntime.disableAutoCompactHint': 'Only manual /compact remains available when enabled.',
'agents.claudeDesktopMapping.customMapping': 'Use Model Aliases',
+1 -1
View File
@@ -569,7 +569,7 @@ export const zhCN = {
'agents.claudeCodeRuntime.maxContextTokensHint': 'Claude Code 默认 200000;请勿超过上游模型实际支持的窗口。',
'agents.claudeCodeRuntime.context1mHint': '为此映射使用 [1m] 模型并将最大窗口设为 1000000;三个映射均关闭后恢复为 200000。',
'agents.claudeCodeRuntime.autoCompactPct': '触发压缩百分比',
'agents.claudeCodeRuntime.autoCompactPctHint': '默认 100;调低后会更早自动压缩。',
'agents.claudeCodeRuntime.autoCompactPctHint': '默认 90%;调低后会更早自动压缩。',
'agents.claudeCodeRuntime.disableAutoCompact': '禁止自动压缩',
'agents.claudeCodeRuntime.disableAutoCompactHint': '开启后仅允许用户通过 /compact 手动压缩。',
'agents.claudeDesktopMapping.customMapping': '使用模型别名',
+18 -11
View File
@@ -39,6 +39,7 @@ import {
} from '../services/agentModelPicker';
import {
resolveAgentConfigurationAction,
resolveAgentModelMappingsDraftSource,
sameAgentModel,
sameAgentModelMappings,
} from '../services/agentConfigurationDraft';
@@ -111,7 +112,7 @@ type ClaudeModelMappings = {
const CODEX_OAUTH_LOGIN_REQUIRED_ERROR = 'CODEX_OAUTH_LOGIN_REQUIRED';
const DEFAULT_CLAUDE_CODE_MAX_CONTEXT_TOKENS = 200_000;
const DEFAULT_CLAUDE_AUTO_COMPACT_PCT = 100;
const DEFAULT_CLAUDE_AUTO_COMPACT_PCT = 90;
const createClaudeModelMappings = (model: string): ClaudeModelMappings => ({
opus: model,
@@ -720,18 +721,24 @@ export function AgentsPage() {
const appliedMappings = selected === 'claude-code'
? activeStatus?.claudeCodeModelMappings
: activeStatus?.claudeDesktopModelMappings;
if (!claudeModelMappingsDirtyRef.current && appliedMappings) {
const appliedModels = claudeMappingRoles
.map((role) => findAgentModel(models, appliedMappings[role.key]))
.filter((model): model is ModelOption => model !== null);
if (appliedModels.length === claudeMappingRoles.length) {
setClaudeCustomMapping(appliedModels.every((model) => Boolean(model.isAlias)));
}
if (!claudeModelMappingsDirtyRef.current) {
const appliedModels = appliedMappings
? claudeMappingRoles
.map((role) => findAgentModel(models, appliedMappings[role.key]))
.filter((model): model is ModelOption => model !== null)
: [];
setClaudeCustomMapping(
appliedModels.length === claudeMappingRoles.length
&& appliedModels.every((model) => Boolean(model.isAlias)),
);
}
setClaudeModelMappingsDraft((current) => {
const source = claudeModelMappingsDirtyRef.current
? current
: appliedMappings ?? current;
const source = resolveAgentModelMappingsDraftSource(
current,
appliedMappings,
createClaudeModelMappings(selectedModel),
claudeModelMappingsDirtyRef.current,
);
const next: ClaudeModelMappings = {
opus: findAgentModel(models, source.opus)?.name ?? selectedModel,
sonnet: findAgentModel(models, source.sonnet)?.name ?? selectedModel,
+8 -1
View File
@@ -50,9 +50,16 @@ export const sameAgentModelMappings = (
&& Boolean(left.sonnet1m) === Boolean(right.sonnet1m)
&& Boolean(left.haiku1m) === Boolean(right.haiku1m)
&& (left.maxContextTokens ?? 200_000) === (right.maxContextTokens ?? 200_000)
&& (left.autoCompactPct ?? 100) === (right.autoCompactPct ?? 100)
&& (left.autoCompactPct ?? 90) === (right.autoCompactPct ?? 90)
&& Boolean(left.disableAutoCompact) === Boolean(right.disableAutoCompact);
export const resolveAgentModelMappingsDraftSource = <T>(
current: T,
applied: T | null | undefined,
fallback: T,
dirty: boolean,
): T => (dirty ? current : applied ?? fallback);
export function resolveAgentConfigurationAction({
client,
modificationState,
+29
View File
@@ -10,6 +10,8 @@ import {
} from '../src/services/agentModelPicker';
import {
resolveAgentConfigurationAction,
resolveAgentModelMappingsDraftSource,
sameAgentModelMappings,
type AgentConfigurationClientId,
} from '../src/services/agentConfigurationDraft';
@@ -98,6 +100,33 @@ const appliedConfiguration = (
});
describe('agent configuration update action', () => {
test('Claude mapping clients do not inherit another client draft', () => {
const codeDraft = { opus: 'code-opus', sonnet: 'code-sonnet', haiku: 'code-haiku' };
const desktopFallback = {
opus: 'desktop-model',
sonnet: 'desktop-model',
haiku: 'desktop-model',
};
expect(resolveAgentModelMappingsDraftSource(
codeDraft,
null,
desktopFallback,
false,
)).toEqual(desktopFallback);
expect(resolveAgentModelMappingsDraftSource(
codeDraft,
null,
desktopFallback,
true,
)).toEqual(codeDraft);
});
test('Claude Code missing auto-compact preference defaults to 90%', () => {
const mappings = { opus: 'model-a', sonnet: 'model-a', haiku: 'model-a' };
expect(sameAgentModelMappings(mappings, { ...mappings, autoCompactPct: 90 })).toBeTrue();
expect(sameAgentModelMappings(mappings, { ...mappings, autoCompactPct: 100 })).toBeFalse();
});
test.each<AgentConfigurationClientId>([
'claude-code',
'claude-desktop',