use super::super::*;
use super::MissionControlApp;
struct SystemPromptWorker {
generation: u64,
handle: super::completion_worker::CompletionWorker<state::SystemPromptModalState>,
}
#[derive(Default)]
pub(super) struct SystemPromptRuntime {
worker: Option<SystemPromptWorker>,
}
impl SystemPromptRuntime {
pub(super) fn is_pending(&self) -> bool {
self.worker.is_some()
}
pub(super) fn cleanup(&mut self) -> Vec<String> {
let Some(worker) = self.worker.take() else {
return Vec::new();
};
match worker.handle.join_with_timeout(WORKER_EXIT_JOIN_TIMEOUT) {
None => vec!["system prompt preparation did not finish before shutdown".into()],
Some(result) => result.err().into_iter().collect(),
}
}
}
impl MissionControlApp {
pub(super) fn start_system_prompt_preparation(&mut self, ui: &mut state::MissionControlState) {
if self.system_prompt.is_pending() {
ui.status = "system prompt preparation already in progress".into();
return;
}
let mut run_config = self
.state
.config
.clone()
.unwrap_or_else(|| self.config.clone());
run_config.model = Some(self.state.model.clone());
let disabled_tools = self.disabled_tools.clone();
let code_mode_setting_enabled = self.settings.tools.code_mode.enabled;
let code_mode_subagents_enabled = code_mode_setting_enabled
&& !self
.settings
.tools
.code_mode
.disabled
.iter()
.any(|name| name == "subagents");
let disabled_profiles = self.disabled_subagent_profiles.clone();
let discovery = self.subagent_profile_discovery.clone();
let instructions = self.instructions.clone();
let skills = self.skills.clone();
let primary_agent = ui.selected_primary_agent_profile();
let handle = super::completion_worker::CompletionWorker::spawn(
"magi-system-prompt",
self.events.clone(),
move || {
let disabled_tools = disabled_tools
.lock()
.map(|disabled| disabled.clone())
.unwrap_or_else(|_| {
crate::tools::MVP_TOOL_CAPABILITIES
.iter()
.map(|tool| tool.canonical_name().to_string())
.collect()
});
let disabled_profiles =
disabled_profiles
.lock()
.map(|disabled| disabled.clone())
.map_err(|_| "disabled subagent profiles lock poisoned".to_string())?;
let discovery = crate::subagents::profiles::filter_enabled_profiles(
&discovery,
&disabled_profiles,
);
let section = if disabled_tools
.contains(crate::tools::contract::tool_name::SUBAGENTS)
&& !code_mode_subagents_enabled
{
None
} else {
crate::subagents::profiles::render_subagent_profiles_prompt(
Some(&run_config.paths.prompts),
&discovery,
)
.map_err(|error| error.to_string())?
};
let mut sections =
crate::agent::build_system_prompt_sections_with_prompt_dir_and_subagents(
Some(&run_config.paths.prompts),
&instructions,
&skills,
section.as_deref(),
code_mode_setting_enabled
&& !disabled_tools.contains(crate::code_mode::TOOL_NAME),
)
.map_err(|error| error.to_string())?;
if let Some(profile) = primary_agent.as_ref() {
sections.push(crate::agent::SystemPromptSection {
title: format!("Primary agent · {}", profile.name),
content: format!(
"\n\n{}",
crate::primary_agents::render_primary_agent_prompt_append(profile)
.trim()
),
});
}
let prompt = sections
.iter()
.map(|section| section.content.as_str())
.collect::<String>();
let provider_id = run_config.provider_id();
let model = run_config
.model
.as_deref()
.unwrap_or_else(|| crate::providers::default_model_for_provider(provider_id));
let count = crate::context::project_text_tokens(provider_id, model, &prompt);
let sections = sections
.into_iter()
.map(|section| {
let tokens = crate::context::project_text_tokens(
provider_id,
model,
§ion.content,
)
.tokens;
state::SystemPromptCard {
title: section.title,
content: section.content,
tokens,
}
})
.collect::<Vec<_>>();
let max_tokens = crate::config::load_context_budget(&run_config)
.unwrap_or_default()
.max_tokens;
let percent = count.tokens.saturating_mul(100) / max_tokens.max(1);
Ok(state::SystemPromptModalState {
content: format!(
"~{} tokens / {} max ({}%) · {} sections\nEstimates include prompt wrappers. Section estimates may not sum to the total.",
count.tokens,
max_tokens,
percent,
sections.len()
),
sections,
})
},
);
match handle {
Ok(handle) => {
self.system_prompt.worker = Some(SystemPromptWorker {
generation: self.session_generation,
handle,
});
ui.open_system_prompt_modal("Preparing system prompt…".into());
ui.status = "preparing system prompt…".into();
}
Err(error) => ui.status = format!("failed to start system prompt preparation: {error}"),
}
}
pub(in crate::tui) fn reconcile_system_prompt_worker(
&mut self,
ui: &mut state::MissionControlState,
) -> bool {
let Some(worker) = self.system_prompt.worker.as_mut() else {
return false;
};
let result = worker.handle.take_result();
let generation = worker.generation;
if worker.handle.ready_to_reap()
&& let Some(worker) = self.system_prompt.worker.take()
{
let _ = worker.handle.join();
}
let Some(result) = result else {
return false;
};
if generation != self.session_generation || !ui.system_prompt_modal_visible() {
return false;
}
match result {
Ok(prompt) => {
ui.show_system_prompt_sections(prompt);
ui.status = "showing current computed system prompt".into();
}
Err(error) => {
ui.open_system_prompt_modal(format!("Failed to compute system prompt: {error}"));
ui.status = "failed to compute system prompt".into();
}
}
true
}
}