use crate::tui::state;
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct PersistedPrimaryAgentResolution {
pub(crate) selected_id: Option<String>,
pub(crate) diagnostics: Vec<crate::primary_agents::PrimaryAgentProfileDiagnostic>,
}
pub(crate) fn resolve_persisted_primary_agent_selection(
selected_id: Option<&str>,
profiles: &std::collections::BTreeMap<String, crate::primary_agents::PrimaryAgentProfile>,
) -> PersistedPrimaryAgentResolution {
let Some(selected_id) = selected_id else {
return PersistedPrimaryAgentResolution {
selected_id: None,
diagnostics: Vec::new(),
};
};
match crate::primary_agents::validate_primary_agent_id(selected_id) {
Ok(id) if profiles.contains_key(&id) => PersistedPrimaryAgentResolution {
selected_id: Some(id),
diagnostics: Vec::new(),
},
Ok(id) => PersistedPrimaryAgentResolution {
selected_id: None,
diagnostics: vec![crate::primary_agents::PrimaryAgentProfileDiagnostic {
id: Some(id),
path: None,
message: "stored primary agent selection is unavailable; using None".to_string(),
}],
},
Err(error) => PersistedPrimaryAgentResolution {
selected_id: None,
diagnostics: vec![crate::primary_agents::PrimaryAgentProfileDiagnostic {
id: Some(sanitized_primary_agent_diagnostic_text(selected_id)),
path: None,
message: format!("stored primary agent selection is invalid; using None: {error}"),
}],
},
}
}
pub(crate) fn record_primary_agent_startup_diagnostics(
state: &mut state::MissionControlState,
diagnostics: &[crate::primary_agents::PrimaryAgentProfileDiagnostic],
) {
if diagnostics.is_empty() {
return;
}
let summary = format!(
"{} primary agent profile diagnostic(s); invalid profiles skipped",
diagnostics.len()
);
state.status = summary.clone();
state.record_error_transcript(&summary);
for diagnostic in diagnostics.iter().take(5) {
state.record_error_transcript(&format!(
"primary agent profile skipped: profile={} reason={}",
sanitized_primary_agent_diagnostic_label(diagnostic),
sanitized_primary_agent_diagnostic_text(&diagnostic.message)
));
}
if diagnostics.len() > 5 {
state.record_error_transcript(&format!(
"{} additional primary agent profile diagnostic(s) omitted",
diagnostics.len() - 5
));
}
}
fn sanitized_primary_agent_diagnostic_label(
diagnostic: &crate::primary_agents::PrimaryAgentProfileDiagnostic,
) -> String {
let label = diagnostic
.path
.as_ref()
.and_then(|path| path.file_name())
.and_then(|name| name.to_str())
.or(diagnostic.id.as_deref())
.unwrap_or("<unknown>");
sanitized_primary_agent_diagnostic_text(label)
}
fn sanitized_primary_agent_diagnostic_text(text: &str) -> String {
let one_line = text.split_whitespace().collect::<Vec<_>>().join(" ");
let lower = one_line.to_ascii_lowercase();
let looks_secret = lower.contains("secret")
|| lower.contains("token")
|| lower.contains("password")
|| lower.contains("apikey")
|| lower.contains("api_key")
|| lower.contains("openai")
|| one_line.starts_with("sk-");
if looks_secret {
"<redacted>".to_string()
} else if one_line.chars().count() > 160 {
format!("{}…", one_line.chars().take(160).collect::<String>())
} else {
one_line
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;
fn profile(id: &str, name: &str, prompt: &str) -> crate::primary_agents::PrimaryAgentProfile {
crate::primary_agents::PrimaryAgentProfile {
id: id.to_string(),
name: name.to_string(),
description: String::new(),
path: PathBuf::from(format!("{id}.md")),
prompt: prompt.to_string(),
}
}
#[test]
fn primary_agent_startup_diagnostics_include_sanitized_actionable_details() {
let mut state = state::MissionControlState::default();
let diagnostics = vec![crate::primary_agents::PrimaryAgentProfileDiagnostic {
id: Some("bad".to_string()),
path: Some(PathBuf::from("bad.md")),
message:
"skipping primary agent profile: frontmatter field 'name' must be a YAML string"
.to_string(),
}];
record_primary_agent_startup_diagnostics(&mut state, &diagnostics);
let transcript = state.transcript_visual_text();
assert_eq!(
state.status,
"1 primary agent profile diagnostic(s); invalid profiles skipped"
);
assert!(transcript.contains("profile=bad.md"), "{transcript}");
assert!(
transcript.contains("frontmatter field 'name' must be a YAML string"),
"{transcript}"
);
}
#[test]
fn primary_agent_startup_diagnostics_redact_secret_like_details() {
let mut state = state::MissionControlState::default();
let diagnostics = vec![crate::primary_agents::PrimaryAgentProfileDiagnostic {
id: Some("OPENAI_TOKEN".to_string()),
path: Some(PathBuf::from("sk-secret.md")),
message: "sk-secret body text must not leak".to_string(),
}];
record_primary_agent_startup_diagnostics(&mut state, &diagnostics);
let transcript = state.transcript_visual_text();
assert!(transcript.contains("profile=<redacted>"), "{transcript}");
assert!(transcript.contains("reason=<redacted>"), "{transcript}");
assert!(!transcript.contains("sk-secret"), "{transcript}");
assert!(!transcript.contains("body text"), "{transcript}");
}
#[test]
fn persisted_primary_agent_resolution_accepts_absent_and_valid_ids() {
let mut profiles = std::collections::BTreeMap::new();
profiles.insert("tars".to_string(), profile("tars", "TARS", "body"));
let absent = resolve_persisted_primary_agent_selection(None, &profiles);
assert_eq!(absent.selected_id, None);
assert!(absent.diagnostics.is_empty());
let valid = resolve_persisted_primary_agent_selection(Some("tars"), &profiles);
assert_eq!(valid.selected_id.as_deref(), Some("tars"));
assert!(valid.diagnostics.is_empty());
}
#[test]
fn persisted_primary_agent_resolution_warns_for_malformed_or_unknown_ids() {
let profiles = std::collections::BTreeMap::new();
let malformed = resolve_persisted_primary_agent_selection(Some("bad/token"), &profiles);
assert_eq!(malformed.selected_id, None);
assert_eq!(malformed.diagnostics.len(), 1);
assert!(malformed.diagnostics[0].message.contains("invalid"));
assert!(!malformed.diagnostics[0].message.contains("prompt body"));
let unknown = resolve_persisted_primary_agent_selection(Some("missing"), &profiles);
assert_eq!(unknown.selected_id, None);
assert_eq!(unknown.diagnostics.len(), 1);
assert!(unknown.diagnostics[0].message.contains("unavailable"));
}
}