feat(agents): add per-role 1M context controls for Claude

This commit is contained in:
lzt404 committed 2026-08-10 18:14:35 +08:00
1 parent 9d7b645c1d
commit 8655f6aae4
14 files changed
+352 -112

No files matched your search

+8 -6
View File
@@ -568,14 +568,16 @@ export const jaOverrides = {
'agents.modify.title': '設定変更',
'agents.modify.oauthConfiguration': 'OAuth ログイン設定方式',
'agents.claudeDesktopMapping.title': 'Claude Desktop モデル → 上流モデル',
'agents.claudeDesktopMapping.description': '各 Claude Desktop モデルが実際に呼び出す上流モデルを選択します。',
'agents.claudeDesktopMapping.description': '役割ごとに上流モデルとコンテキスト機能を選択します。対応モデルでのみ 1M を有効にしてください。',
'agents.claudeCodeMapping.title': 'Claude Code モデル → 上流モデル',
'agents.claudeCodeMapping.description': '各タスク役割に対して使用する上流モデルを選択します。',
'agents.claudeDesktopMapping.customMapping': 'カスタムマッピング',
'agents.claudeCodeMapping.description': 'タスク役割ごとに上流モデルとコンテキスト機能を選択します。対応モデルでのみ 1M を有効にしてください。',
'agents.claudeDesktopMapping.customMapping': 'モデルエイリアスを使用',
'agents.claudeDesktopMapping.customMappingHint': '有効にするとエイリアスモデルのみ、無効にするとエイリアス未設定の元モデルのみ表示します。',
'agents.claudeDesktopMapping.opus': 'Claude Opus 5 のマッピング先',
'agents.claudeDesktopMapping.sonnet': 'Claude Sonnet 4.6 のマッピング先',
'agents.claudeDesktopMapping.haiku': 'Claude Haiku 4.5 のマッピング先',
'agents.claudeDesktopMapping.opus': 'Claude Opus 5',
'agents.claudeDesktopMapping.sonnet': 'Claude Sonnet 4.6',
'agents.claudeDesktopMapping.haiku': 'Claude Haiku 4.5',
'agents.claudeMapping.context1m': '1M',
'agents.claudeMapping.context1mHint': '1M コンテキストとして設定します。選択した上流モデルが対応していることを確認してください。',
'agents.oauthLoginRequired.title': 'Codex へのログインが必要です',
'agents.oauthLoginRequired.enableDescription': '有効な Codex のログイン認証情報が見つからないため、OAuth 設定方式を有効にできません。Codex アプリで ChatGPT アカウントにログインしてください。Codex アプリでログインを開始できない場合は、先に',
'agents.oauthLoginRequired.enableClearConfiguration': '設定を消去',
+8 -6
View File
@@ -563,14 +563,16 @@ export const en: Record<MessageKey, string> = {
'agents.useModel': 'Model',
'agents.pending': 'Not Applied',
'agents.claudeDesktopMapping.title': 'Claude Desktop Model → Upstream Model',
'agents.claudeDesktopMapping.description': 'Choose the upstream model each Claude Desktop model actually calls.',
'agents.claudeDesktopMapping.description': 'Choose the upstream model and context capability for each role. Turn on 1M only for supported models.',
'agents.claudeCodeMapping.title': 'Claude Code Model → Upstream Model',
'agents.claudeCodeMapping.description': 'Choose the upstream model used for each task role.',
'agents.claudeDesktopMapping.customMapping': 'Custom Mapping',
'agents.claudeCodeMapping.description': 'Choose the upstream model and context capability for each task role. Turn on 1M only for supported models.',
'agents.claudeDesktopMapping.customMapping': 'Use Model Aliases',
'agents.claudeDesktopMapping.customMappingHint': 'When enabled, only aliased models are shown; when disabled, only original models without aliases are shown.',
'agents.claudeDesktopMapping.opus': 'Map Claude Opus 5 to',
'agents.claudeDesktopMapping.sonnet': 'Map Claude Sonnet 4.6 to',
'agents.claudeDesktopMapping.haiku': 'Map Claude Haiku 4.5 to',
'agents.claudeDesktopMapping.opus': 'Claude Opus 5',
'agents.claudeDesktopMapping.sonnet': 'Claude Sonnet 4.6',
'agents.claudeDesktopMapping.haiku': 'Claude Haiku 4.5',
'agents.claudeMapping.context1m': '1M',
'agents.claudeMapping.context1mHint': 'Configures a 1M context window. Make sure the selected upstream model supports it.',
'agents.model.readingAvailable': 'Loading available models',
'agents.model.current': 'Currently applied: {model}',
'agents.model.firstSelection': '{count} models available; the first is selected by default',
+8 -6
View File
@@ -561,14 +561,16 @@ export const zhCN = {
'agents.useModel': '使用模型',
'agents.pending': '尚未应用',
'agents.claudeDesktopMapping.title': 'Claude Desktop 模型 → 上游模型',
'agents.claudeDesktopMapping.description': '选择每个 Claude Desktop 模型实际调用的上游模型。',
'agents.claudeDesktopMapping.description': '分别选择上游模型和上下文能力;仅为确认支持的模型打开 1M。',
'agents.claudeCodeMapping.title': 'Claude Code 模型 → 上游模型',
'agents.claudeCodeMapping.description': '为不同任务角色选择使用的上游模型。',
'agents.claudeDesktopMapping.customMapping': '自定义映射',
'agents.claudeCodeMapping.description': '分别为不同任务角色选择上游模型和上下文能力;仅为确认支持的模型打开 1M。',
'agents.claudeDesktopMapping.customMapping': '使用模型别名',
'agents.claudeDesktopMapping.customMappingHint': '开启后只显示别名模型;关闭后只显示未设置别名的原始模型。',
'agents.claudeDesktopMapping.opus': 'Claude Opus 5 映射到',
'agents.claudeDesktopMapping.sonnet': 'Claude Sonnet 4.6 映射到',
'agents.claudeDesktopMapping.haiku': 'Claude Haiku 4.5 映射到',
'agents.claudeDesktopMapping.opus': 'Claude Opus 5',
'agents.claudeDesktopMapping.sonnet': 'Claude Sonnet 4.6',
'agents.claudeDesktopMapping.haiku': 'Claude Haiku 4.5',
'agents.claudeMapping.context1m': '1M',
'agents.claudeMapping.context1mHint': '打开后按 1M 上下文配置,请确认所选上游模型支持。',
'agents.model.readingAvailable': '正在读取可用模型',
'agents.model.current': '当前已应用:{model}',
'agents.model.firstSelection': '{count} 个可用模型,首次默认选择第一项',
+46 -2
View File
@@ -101,6 +101,9 @@ type ClaudeModelMappings = {
opus: string;
sonnet: string;
haiku: string;
opus1m: boolean;
sonnet1m: boolean;
haiku1m: boolean;
};
const CODEX_OAUTH_LOGIN_REQUIRED_ERROR = 'CODEX_OAUTH_LOGIN_REQUIRED';
@@ -109,19 +112,25 @@ const createClaudeModelMappings = (model: string): ClaudeModelMappings => ({
opus: model,
sonnet: model,
haiku: model,
opus1m: false,
sonnet1m: false,
haiku1m: false,
});
const claudeMappingRoles = [
{
key: 'opus',
contextKey: 'opus1m',
labelKey: 'agents.claudeDesktopMapping.opus',
},
{
key: 'sonnet',
contextKey: 'sonnet1m',
labelKey: 'agents.claudeDesktopMapping.sonnet',
},
{
key: 'haiku',
contextKey: 'haiku1m',
labelKey: 'agents.claudeDesktopMapping.haiku',
},
] as const;
@@ -719,6 +728,9 @@ export function AgentsPage() {
opus: findAgentModel(models, source.opus)?.name ?? selectedModel,
sonnet: findAgentModel(models, source.sonnet)?.name ?? selectedModel,
haiku: findAgentModel(models, source.haiku)?.name ?? selectedModel,
opus1m: Boolean(source.opus1m),
sonnet1m: Boolean(source.sonnet1m),
haiku1m: Boolean(source.haiku1m),
};
return sameAgentModelMappings(current, next) ? current : next;
});
@@ -809,7 +821,7 @@ export function AgentsPage() {
};
const selectClaudeModelMapping = (
role: keyof ClaudeModelMappings,
role: 'opus' | 'sonnet' | 'haiku',
value: string,
) => {
const model = findAgentModel(models, value);
@@ -819,6 +831,14 @@ export function AgentsPage() {
setClaudeModelMappingsDraft((current) => ({ ...current, [role]: model.name }));
};
const changeClaude1mPreference = (
preference: 'opus1m' | 'sonnet1m' | 'haiku1m',
enabled: boolean,
) => {
claudeModelMappingsDirtyRef.current = true;
setClaudeModelMappingsDraft((current) => ({ ...current, [preference]: enabled }));
};
const changeClaudeCustomMapping = (enabled: boolean) => {
setClaudeCustomMapping(enabled);
setModelSelectionError('');
@@ -827,6 +847,9 @@ export function AgentsPage() {
opus: resolveAgentModelForAliasMode(models, current.opus, enabled),
sonnet: resolveAgentModelForAliasMode(models, current.sonnet, enabled),
haiku: resolveAgentModelForAliasMode(models, current.haiku, enabled),
opus1m: current.opus1m,
sonnet1m: current.sonnet1m,
haiku1m: current.haiku1m,
};
if (!sameAgentModelMappings(current, next)) {
claudeModelMappingsDirtyRef.current = true;
@@ -863,6 +886,7 @@ export function AgentsPage() {
return null;
}
resolved[role.key] = model.name;
resolved[role.contextKey] = claudeModelMappingsDraft[role.contextKey];
}
return resolved;
};
@@ -1270,7 +1294,27 @@ export function AgentsPage() {
<div className="agent-claude-desktop-mapping-grid">
{claudeMappingRoles.map((role) => (
<div className="agent-claude-desktop-mapping-row" key={role.key}>
<strong>{t(role.labelKey)}</strong>
<div className="agent-claude-mapping-card-heading">
<strong>{t(role.labelKey)}</strong>
<label
className="agent-claude-context-toggle"
title={t('agents.claudeMapping.context1mHint')}
>
<span>{t('agents.claudeMapping.context1m')}</span>
<span className="switch-control">
<input
type="checkbox"
checked={claudeModelMappingsDraft[role.contextKey]}
onChange={(event) => changeClaude1mPreference(
role.contextKey,
event.currentTarget.checked,
)}
disabled={busy || modelLoading}
/>
<span className="switch-track" />
</span>
</label>
</div>
<AgentModelPicker
models={claudeMappingModels}
value={claudeModelMappingsDraft[role.key]}
+7 -1
View File
@@ -15,6 +15,9 @@ export type AgentModelMappings = {
opus: string;
sonnet: string;
haiku: string;
opus1m?: boolean;
sonnet1m?: boolean;
haiku1m?: boolean;
};
type ResolveAgentConfigurationActionOptions = {
@@ -39,7 +42,10 @@ export const sameAgentModelMappings = (
right: AgentModelMappings,
) => sameAgentModel(left.opus, right.opus)
&& sameAgentModel(left.sonnet, right.sonnet)
&& sameAgentModel(left.haiku, right.haiku);
&& sameAgentModel(left.haiku, right.haiku)
&& Boolean(left.opus1m) === Boolean(right.opus1m)
&& Boolean(left.sonnet1m) === Boolean(right.sonnet1m)
&& Boolean(left.haiku1m) === Boolean(right.haiku1m);
export function resolveAgentConfigurationAction({
client,
+55 -3
View File
@@ -5516,18 +5516,67 @@ textarea:focus-visible {
.agent-claude-desktop-mapping-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 10px;
gap: 12px;
}
.agent-claude-desktop-mapping-row {
display: grid;
min-width: 0;
gap: 7px;
align-content: start;
gap: 10px;
padding: 12px;
border: 1px solid var(--theme-e3e1db);
border-radius: 9px;
background: var(--theme-fff);
box-shadow: 0 1px 2px rgba(45, 42, 38, 0.04);
}
.agent-claude-desktop-mapping-row > strong {
.agent-claude-desktop-mapping-row:has(.agent-claude-context-toggle input:checked) {
border-color: rgba(128, 105, 173, 0.48);
box-shadow: 0 0 0 2px rgba(128, 105, 173, 0.08);
}
.agent-claude-mapping-card-heading {
display: flex;
min-width: 0;
align-items: center;
justify-content: space-between;
gap: 8px;
}
.agent-claude-mapping-card-heading > strong {
min-width: 0;
overflow: hidden;
color: var(--theme-5f5a53);
font-size: var(--font-size-label);
text-overflow: ellipsis;
white-space: nowrap;
}
.agent-claude-context-toggle {
display: flex;
flex: none;
align-items: center;
gap: 6px;
padding: 4px 7px;
border: 1px solid var(--theme-e3e1db);
border-radius: 999px;
background: var(--theme-faf9f5);
color: var(--theme-5f5a53);
cursor: pointer;
font-size: var(--font-size-label);
font-weight: 700;
}
.agent-claude-context-toggle:has(input:checked) {
border-color: var(--theme-8069ad);
background: var(--theme-f1ecfb);
color: var(--theme-554075);
}
.agent-claude-context-toggle:has(input:disabled) {
cursor: not-allowed;
opacity: 0.62;
}
.agent-section-heading-actions {
@@ -7215,6 +7264,9 @@ textarea:focus-visible {
}
@container agent-panel (max-width: 760px) {
.agent-claude-desktop-mapping-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@container agent-panel (max-width: 640px) {