From 60a3683976d04adbd9777e9725e2e2a883f589a1 Mon Sep 17 00:00:00 2001 From: lzt404 <2596933790@qq.com> Date: Thu, 10 Sep 2026 20:27:36 +0800 Subject: [PATCH] fix(agents): record model mapping changes without file changes Include Claude Desktop mappings in history change detection and preserve the previous selection. Show mapping changes in history previews and invalidate previews when metadata changes. Validation: 15 history tests pass, including identical-profile model changes and repeated no-op saves. --- src-tauri/src/agents/history.rs | 31 +++++++++++++++++--- src-tauri/src/agents/history/tests.rs | 41 +++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 4 deletions(-) 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); +}