diff --git a/desktop/src-tauri/src/commands/agents.rs b/desktop/src-tauri/src/commands/agents.rs index 398fc7b719..7cdb7a361e 100644 --- a/desktop/src-tauri/src/commands/agents.rs +++ b/desktop/src-tauri/src/commands/agents.rs @@ -46,7 +46,13 @@ fn build_deploy_payload( // Resolve system prompt and model via the persona (same as local spawn). // Re-reads the persona store so edits take effect without recreating the agent. - let personas = load_personas(app).unwrap_or_default(); + let personas = load_personas(app).unwrap_or_else(|e| { + eprintln!( + "sprout-desktop: failed to load personas for deploy payload (agent {}) — proceeding without persona context: {e}", + record.name + ); + Vec::new() + }); let (effective_prompt, effective_model) = crate::managed_agents::resolve_effective_prompt_and_model( record.persona_id.as_deref(), diff --git a/desktop/src-tauri/src/managed_agents/runtime.rs b/desktop/src-tauri/src/managed_agents/runtime.rs index baee5f443b..80f1f64685 100644 --- a/desktop/src-tauri/src/managed_agents/runtime.rs +++ b/desktop/src-tauri/src/managed_agents/runtime.rs @@ -672,7 +672,13 @@ pub fn spawn_agent_child( // Resolve system prompt and model via the extracted helper. Re-reads // the persona store at spawn time so edits take effect without // deleting and recreating the agent. - let personas = super::load_personas(app).unwrap_or_default(); + let personas = super::load_personas(app).unwrap_or_else(|e| { + eprintln!( + "sprout-desktop: failed to load personas for agent {} — spawning with no persona context: {e}", + record.name + ); + Vec::new() + }); let (effective_prompt, effective_model) = resolve_effective_prompt_and_model( record.persona_id.as_deref(), &personas, diff --git a/desktop/src-tauri/src/managed_agents/storage.rs b/desktop/src-tauri/src/managed_agents/storage.rs index f373a1226d..70e661e1da 100644 --- a/desktop/src-tauri/src/managed_agents/storage.rs +++ b/desktop/src-tauri/src/managed_agents/storage.rs @@ -40,7 +40,36 @@ pub fn load_managed_agents(app: &AppHandle) -> Result, S let content = fs::read_to_string(&path) .map_err(|error| format!("failed to read agent store: {error}"))?; - serde_json::from_str(&content).map_err(|error| format!("failed to parse agent store: {error}")) + let mut records: Vec = + serde_json::from_str(&content).map_err(|error| format!("failed to parse agent store: {error}"))?; + + // One-time migration: clear stale system_prompt/model snapshots on + // persona-backed agents. Before this fix, creation snapshotted the + // persona's values onto the agent record. With the new precedence logic + // (agent record wins when set), those stale snapshots would be treated as + // explicit user overrides and block persona updates from taking effect. + // + // Safe to clear unconditionally: the old code ignored agent-level overrides + // for persona-backed agents, so no user has ever set a meaningful override. + let mut migrated = false; + for record in &mut records { + if record.persona_id.is_some() { + if record.system_prompt.is_some() { + record.system_prompt = None; + migrated = true; + } + if record.model.is_some() { + record.model = None; + migrated = true; + } + } + } + if migrated { + // Persist the migration so it only runs once. + save_managed_agents(app, &records)?; + } + + Ok(records) } pub fn save_managed_agents(app: &AppHandle, records: &[ManagedAgentRecord]) -> Result<(), String> {