use std::collections::VecDeque;
use std::sync::{Arc, Mutex};
use tokio::sync::mpsc;
use tokio::task::JoinHandle;
use crate::agent::plan::runtime::{ActivePlan, ReviewPhaseHandle, spawn_review};
use crate::agent::plan::workflow::{
REVIEWER_TOOLS, ReviewStep, implement_retry_prompt, reviewer_prompt,
};
use crate::agent::tools::background::{BackgroundStore, prepend_pending_notifications};
use crate::event::AgentEvent;
use crate::provider::AnyAgent;
use crate::session::{MessageRole, Session, ToolCallEntry};
use crate::ui::colors::{c_agent, c_error};
use crate::ui::renderer::Renderer;
use crate::ui::run_handlers::RunCtx;
pub(super) fn drive_plan_review(
ctx: &mut RunCtx<'_>,
agent: &AnyAgent,
response: &str,
tool_calls: &[ToolCallEntry],
review_phase: &mut Option<ReviewPhaseHandle>,
is_running: &mut bool,
) -> anyhow::Result<()> {
if *is_running {
return Ok(());
}
let Some(active) = ctx.active_plan.take() else {
return Ok(());
};
let transcript = crate::agent::review::build_transcript(ctx.session);
ctx.renderer
.write_line("Phase: Review — reviewer runs the code…", c_agent())?;
let runner =
agent.spawn_phase_runner(reviewer_prompt(&active.plan), transcript, REVIEWER_TOOLS);
*review_phase = Some(spawn_review(
runner,
active.plan,
active.cycles_left,
response.to_string(),
tool_calls.to_vec(),
));
*is_running = true;
Ok(())
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn apply_review_verdict(
result: Result<ReviewStep, String>,
plan: String,
cycles_left: usize,
response: &str,
tool_calls: &[ToolCallEntry],
renderer: &mut Renderer,
session: &mut Session,
active_plan: &mut Option<ActivePlan>,
last_user_prompt: &mut String,
agent: &AnyAgent,
bg_store: &Option<BackgroundStore>,
interjection_queue: &Arc<Mutex<VecDeque<String>>>,
agent_rx: &mut Option<mpsc::Receiver<AgentEvent>>,
agent_abort: &mut Option<JoinHandle<()>>,
agent_interject: &mut Option<mpsc::Sender<()>>,
agent_cancel: &mut Option<mpsc::Sender<()>>,
is_running: &mut bool,
) -> anyhow::Result<()> {
if let Ok(ReviewStep::Retry { feedback }) = &result {
renderer.write_line(
"Phase: Review — changes needed; re-implementing…",
c_agent(),
)?;
let retry_prompt = implement_retry_prompt(feedback);
last_user_prompt.clone_from(&retry_prompt);
session.add_message(MessageRole::User, &retry_prompt);
let runner = agent.clone().spawn_runner(
prepend_pending_notifications(&retry_prompt, bg_store.as_ref()),
crate::agent::runner::convert_history(session),
Some(interjection_queue.clone()),
);
runner.install_into(
agent_rx,
agent_abort,
agent_interject,
agent_cancel,
is_running,
);
*active_plan = Some(ActivePlan {
plan,
cycles_left: cycles_left - 1,
});
return Ok(());
}
match result {
Ok(ReviewStep::Approved) => {
renderer.write_line("Phase: Review — ✓ reviewer approved", c_agent())?;
}
Ok(ReviewStep::Exhausted) => {
renderer.write_line(
"Phase: Review — fix-cycle budget spent; stopping. Continue manually if needed.",
c_agent(),
)?;
}
Err(e) => {
renderer.write_line(
&format!("Phase: Review — reviewer error: {e}; stopping"),
c_error(),
)?;
}
Ok(ReviewStep::Retry { .. }) => unreachable!("Retry handled above"),
}
*is_running = false;
super::done::finalize_idle_turn(
session,
last_user_prompt,
response,
tool_calls,
agent,
bg_store,
interjection_queue,
agent_rx,
agent_abort,
agent_interject,
agent_cancel,
is_running,
)
}