use std::sync::mpsc::Sender; use anyhow::Result; use crate::app::AppEvent; use crate::model::{ControllerPhase, PlannerResponse, PlanningTurn, SessionSource, TaskConfig}; use crate::process; use crate::storage::toon; pub fn advance( repo_root: &std::path::Path, config: &TaskConfig, latest_user_input: &str, event_tx: &Sender, ) -> Result { let mut state = toon::read_state(&config.state_file)?; let goal_md = toon::read_markdown(&config.goal_file)?; let standards_md = toon::read_markdown(&config.standards_file)?; state.phase = ControllerPhase::Planning; state.clear_stop_reason(); state.planning_session.transcript.push(PlanningTurn { role: "user".to_string(), content: latest_user_input.to_string(), }); toon::write_state(&config.state_file, &state)?; let prompt = crate::planning::forwarder::build_planning_prompt( config, &goal_md, &standards_md, &state, latest_user_input, ); let raw = process::run_codex_with_schema( repo_root, &prompt, &crate::planning::forwarder::planning_schema(), event_tx, SessionSource::Planner, Some(config.controller_id()), )?; let response = crate::planning::forwarder::parse_planning_response(&raw)?; match response.kind.as_str() { "question" => { if let Some(question) = &response.question { state.planning_session.pending_question = Some(question.clone()); state.planning_session.transcript.push(PlanningTurn { role: "assistant".to_string(), content: question.clone(), }); toon::write_state(&config.state_file, &state)?; } } "final" => { let next_goal = response.goal_md.clone().unwrap_or(goal_md); let next_standards = response.standards_md.clone().unwrap_or(standards_md); let plan = response.plan.clone().unwrap_or_default(); toon::write_markdown(&config.goal_file, &next_goal)?; toon::write_markdown(&config.standards_file, &next_standards)?; toon::write_plan(&config.plan_file, &plan)?; state.phase = ControllerPhase::Executing; state.clear_stop_reason(); state.goal_revision += 1; state.goal_status = crate::model::GoalStatus::InProgress; state.replan_required = false; state.planning_session.pending_question = None; state.planning_session.transcript.push(PlanningTurn { role: "assistant".to_string(), content: "Planning completed".to_string(), }); toon::write_state(&config.state_file, &state)?; } _ => {} } Ok(response) }