diff --git a/src-tauri/src/agents/history.rs b/src-tauri/src/agents/history.rs index 578667a..4b0306e 100644 --- a/src-tauri/src/agents/history.rs +++ b/src-tauri/src/agents/history.rs @@ -610,7 +610,9 @@ fn commit_history_with_writer( if image_revision(&history_images(paths)?) != image_revision(before) { return Err("配置已被其他程序修改,请刷新后重试".into()); } - if changed.is_empty() { + let prior_mappings = matching_desktop_mappings(client, paths, before); + let next_mappings = mappings.or_else(|| matching_desktop_mappings(client, paths, after)); + if changed.is_empty() && prior_mappings == next_mappings { return Ok(action_result( "unchanged", true, @@ -620,9 +622,12 @@ fn commit_history_with_writer( )); } let mut prior = make_version(client, paths, before, &format!("before-{source}"), None); - prior.mappings = matching_desktop_mappings(client, paths, before); + prior.mappings = prior_mappings; + if let Some(mappings) = prior.mappings.as_ref() { + prior.model = Some(mappings.opus.clone()); + } let mut next = make_version(client, paths, after, source, model.clone()); - next.mappings = mappings.or_else(|| matching_desktop_mappings(client, paths, after)); + next.mappings = next_mappings; write_version(paths, &prior)?; if image_revision(&history_images(paths)?) != image_revision(before) { return Err("备份期间配置发生变化,请刷新后重试".into()); @@ -823,9 +828,27 @@ fn preview( }); } } + let current_mappings = matching_desktop_mappings(client, paths, ¤t); + if client == "claude-desktop" && current_mappings != version.mappings { + let before = serde_json::to_value(¤t_mappings).map_err(|e| e.to_string())?; + let after = serde_json::to_value(&version.mappings).map_err(|e| e.to_string())?; + let mut fields = BTreeSet::new(); + collect_changes(Some(&before), Some(&after), Vec::new(), &mut fields); + for field in fields { + differences.push(HistoryDifference { + file: path_to_string(&paths[2]), + field: format!("modelMappings.{}", field.join(".")), + before: hidden(get(&before, &field), &field), + after: hidden(get(&after, &field), &field), + }); + } + } + let revision = sha256_bytes( + format!("{}:{}", image_revision(¤t), serde_json::to_string(¤t_mappings).map_err(|e| e.to_string())?).as_bytes(), + ); Ok(( HistoryPreview { - revision: image_revision(¤t), + revision, differences, }, current, diff --git a/src-tauri/src/agents/history/tests.rs b/src-tauri/src/agents/history/tests.rs index 3b065f4..776aa12 100644 --- a/src-tauri/src/agents/history/tests.rs +++ b/src-tauri/src/agents/history/tests.rs @@ -405,3 +405,44 @@ fn directory_at_config_path_is_an_error() { fs::create_dir_all(&paths[0]).unwrap(); assert!(history_images(&paths).is_err()); } + +fn desktop_test_models() -> Vec { + ["model-a", "model-b"].into_iter().map(|name| AgentModelOption { + name: name.into(), alias: None, is_alias: false, context_window: Some(200_000), + }).collect() +} + +fn apply_desktop_test_model(home: &Path, model: &str) -> AgentConfigActionResult { + let mappings = ClaudeDesktopModelMappings::all(model); + apply_agent_configuration_with_oauth( + AgentClient::ClaudeDesktop, home, 8317, "test-key", model, + AgentConfigurationOptions { + models: &desktop_test_models(), codex_catalog: None, oauth_configuration: false, + claude_code_model_mappings: None, claude_desktop_model_mappings: Some(&mappings), + }, + ).unwrap() +} + +#[test] +fn desktop_mapping_changes_are_saved_when_profile_bytes_are_identical() { + let home = Home::new(); + let paths = history_paths("claude-desktop", &home.0).unwrap(); + let first = apply_desktop_test_model(&home.0, "model-a"); + let images = history_images(&paths).unwrap(); + let first_id = first.history_version.unwrap(); + let original_preview = preview("claude-desktop", &paths, &first_id).unwrap().0; + let second = apply_desktop_test_model(&home.0, "model-b"); + assert_eq!(second.outcome, "updated"); + assert!(second.changed_files.is_empty()); + assert!(second.history_version.is_some()); + assert_eq!(images, history_images(&paths).unwrap()); + assert_eq!(versions("claude-desktop", &paths).unwrap().0.len(), 4); + assert_eq!(current_desktop_history_mappings(&home.0).unwrap().opus, "model-b"); + let next_preview = preview("claude-desktop", &paths, &first_id).unwrap().0; + assert_ne!(original_preview.revision, next_preview.revision); + assert!(next_preview.differences.iter().any(|diff| diff.field == "modelMappings.opus")); + let unchanged = apply_desktop_test_model(&home.0, "model-b"); + assert_eq!(unchanged.outcome, "unchanged"); + assert!(unchanged.history_version.is_none()); + assert_eq!(versions("claude-desktop", &paths).unwrap().0.len(), 4); +}