use anyhow::Result;
use std::path::Path;
use vtcode_core::primary_agent::{ActivePrimaryAgent, ActivePrimaryAgentState};
use vtcode_core::tools::registry::ToolRegistry;
use crate::agent::runloop::unified::planning_workflow::PlanExecutionContext;
use crate::agent::runloop::unified::turn::primary_agent_runtime::{
builtin_primary_agent_specs, load_primary_agent_specs, resolve_approved_plan_execution_agent,
};
pub(super) const PLAN_APPROVED_EXECUTION_DIRECTIVE: &str = "Execution handoff is active. Any earlier message saying tools are disabled or implementation is paused belongs to the completed planning/recovery turn and is stale. Tools are enabled now. Do not report that work is paused, ask to wait, or request another confirmation. Start implementation immediately: execute the approved plan step by step beginning with the first pending step. Before the first implementation action, use task_tracker with action=list and mark the first pending task in_progress; update each task as work and verification complete. Use cargo nextest run --locked for Rust verification; never emit a raw <tool_call> block as text. Finish with a concise execution summary covering the outcome, changed files, verification performed, and remaining blockers.";
pub(super) const PLAN_APPROVED_FRESH_CONTEXT_HEADER: &str = "This is a fresh execution context. The persisted approved plan below is the source of user intent. Treat it as authoritative and implement it now.";
pub(super) const PLAN_APPROVED_EXECUTION_INPUT: &str = "Implement the approved plan now.";
pub(super) fn build_approved_plan_execution_prompt(
execution_context: PlanExecutionContext,
plan_seed: Option<&str>,
) -> String {
let mut prompt = String::new();
if matches!(execution_context, PlanExecutionContext::Fresh) {
prompt.push_str(PLAN_APPROVED_FRESH_CONTEXT_HEADER);
prompt.push_str("\n\n");
}
prompt.push_str(PLAN_APPROVED_EXECUTION_DIRECTIVE);
if let Some(seed) = plan_seed.filter(|seed| !seed.trim().is_empty()) {
prompt.push_str("\n\nApproved plan context:\n");
prompt.push_str(seed);
}
prompt
}
pub(super) async fn apply_primary_agent_tool_policy_overrides(
tool_registry: &ToolRegistry,
active_agent: &ActivePrimaryAgent,
) {
for (tool_name, policy) in &active_agent.tool_policy_overrides {
if let Err(err) = tool_registry.set_tool_policy(tool_name, policy.clone()).await {
tracing::warn!(
tool = %tool_name,
error = %err,
"Failed to apply restored primary-agent tool policy"
);
}
}
}
/// Select the write-capable agent that must own an approved-plan execution.
///
/// Approval is a hard runtime boundary. If discovery returns a stale or
/// read-only catalog, falling back to the built-in `build` spec is safer than
/// continuing with the planning agent and letting the first mutation fail
/// under the planning policy.
pub(super) async fn select_approved_plan_execution_agent(
active_primary_agent: &mut ActivePrimaryAgentState,
tool_registry: &ToolRegistry,
workspace: &Path,
requested_agent: Option<&str>,
configured_default: Option<&str>,
) -> Result<String> {
let mut specs = match load_primary_agent_specs(tool_registry, workspace).await {
Ok(specs) => specs,
Err(err) => {
tracing::warn!(error = %err, "Primary-agent discovery failed during approved-plan handoff; using built-ins");
builtin_primary_agent_specs()
}
};
let mut execution_agent = resolve_approved_plan_execution_agent(requested_agent, configured_default, &specs);
if execution_agent.is_none() {
specs = builtin_primary_agent_specs();
execution_agent = resolve_approved_plan_execution_agent(requested_agent, configured_default, &specs);
}
let execution_agent = execution_agent.ok_or_else(|| {
anyhow::anyhow!(
"No write-capable primary agent is available to execute the approved plan; the plan was not started"
)
})?;
active_primary_agent
.select_from_specs(&specs, &execution_agent)
.map_err(|err| {
anyhow::anyhow!("Failed to activate approved-plan execution agent '{execution_agent}': {err}")
})?;
Ok(execution_agent)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fresh_execution_prompt_starts_with_fresh_context_header() {
let prompt = build_approved_plan_execution_prompt(PlanExecutionContext::Fresh, Some("step: build"));
assert!(prompt.starts_with(PLAN_APPROVED_FRESH_CONTEXT_HEADER));
}
#[test]
fn approved_plan_is_appended_after_handoff_instructions() {
let plan_seed = "step: build the feature";
let prompt = build_approved_plan_execution_prompt(PlanExecutionContext::Fresh, Some(plan_seed));
let directive_end = prompt
.find(PLAN_APPROVED_EXECUTION_DIRECTIVE)
.expect("execution directive should be present")
+ PLAN_APPROVED_EXECUTION_DIRECTIVE.len();
let plan_start = prompt.find(plan_seed).expect("approved plan should be present");
assert!(plan_start > directive_end);
}
#[test]
fn current_context_prompt_does_not_use_fresh_context_header() {
let prompt = build_approved_plan_execution_prompt(PlanExecutionContext::Current, None);
assert!(prompt.starts_with(PLAN_APPROVED_EXECUTION_DIRECTIVE));
assert!(!prompt.contains(PLAN_APPROVED_FRESH_CONTEXT_HEADER));
}
#[test]
fn empty_plan_seed_does_not_create_empty_context_section() {
let prompt = build_approved_plan_execution_prompt(PlanExecutionContext::Current, Some(" \n\t"));
assert_eq!(prompt, PLAN_APPROVED_EXECUTION_DIRECTIVE);
assert!(!prompt.contains("Approved plan context:"));
}
}