use crate::cm_config::{AgentConfig, FinalPlanRequirementMode};
use super::turn_orchestration::TurnResolution;
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct TurnRouteDecisionV1 {
pub version: u8,
pub turn_start: TurnStartSnapshot,
pub orchestration_mode: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub freeform_because: Option<String>,
pub planner_executor_mode: String,
pub plan_requirement_policy: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub orchestration_profile: Option<String>,
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[serde(tag = "outcome", rename_all = "snake_case")]
pub enum TurnStartSnapshot {
Disabled,
EmptyTask,
ActHeuristics {
review_readonly: bool,
},
}
impl TurnRouteDecisionV1 {
pub fn to_json(&self) -> Result<String, serde_json::Error> {
serde_json::to_string(self)
}
}
fn plan_requirement_policy_label(cfg: &AgentConfig) -> String {
match cfg.per_plan_policy.final_plan_requirement {
FinalPlanRequirementMode::Never => "never".to_string(),
FinalPlanRequirementMode::WorkflowReflection => "workflow_reflection".to_string(),
FinalPlanRequirementMode::Always => "always".to_string(),
}
}
pub fn build_turn_route_decision(
cfg: &AgentConfig,
turn_start: TurnStartSnapshot,
entry: &TurnResolution,
) -> TurnRouteDecisionV1 {
TurnRouteDecisionV1 {
version: 1,
turn_start,
orchestration_mode: entry.orchestration_mode.as_str().to_string(),
freeform_because: entry.freeform_because.map(|b| b.as_str().to_string()),
planner_executor_mode: cfg
.per_plan_policy
.planner_executor_mode
.as_str()
.to_string(),
plan_requirement_policy: plan_requirement_policy_label(cfg),
orchestration_profile: Some(
cfg.per_plan_policy
.orchestration_profile
.as_str()
.to_string(),
),
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TurnRouteDriver {
ReAct,
}
#[derive(Debug, Clone, PartialEq)]
pub struct AssessedTurnRoute {
pub decision: TurnRouteDecisionV1,
pub driver: TurnRouteDriver,
}
#[derive(Debug)]
pub struct AssessTurnRoutingParams<'a> {
pub cfg: &'a AgentConfig,
pub turn_start: TurnStartSnapshot,
}
pub fn assess_turn_routing(params: AssessTurnRoutingParams<'_>) -> AssessedTurnRoute {
let entry = TurnResolution::resolve_react(params.cfg);
let decision = build_turn_route_decision(params.cfg, params.turn_start.clone(), &entry);
AssessedTurnRoute {
decision,
driver: TurnRouteDriver::ReAct,
}
}
pub fn log_turn_route_decision(decision: &TurnRouteDecisionV1) {
log::info!(
target: "crabmate::agent_turn",
"turn_route_decision version={} orchestration_mode={} freeform_because={} planner_executor_mode={} plan_requirement_policy={}",
decision.version,
decision.orchestration_mode,
decision.freeform_because.as_deref().unwrap_or(""),
decision.planner_executor_mode,
decision.plan_requirement_policy,
);
}