pub mod final_plan_gate;
mod final_plan_gate_context;
mod final_plan_gate_reason;
mod per_turn_state;
pub use crate::cm_config::FinalPlanRequirementMode;
use crate::cm_config::AgentConfig;
use crate::cm_types::Message;
use crate::cm_agent::plan_artifact;
use crate::cm_agent::plan_rewrite;
use crate::cm_agent::workflow_reflection_controller::{self, WorkflowReflectionController};
use serde_json::Value;
pub use plan_rewrite::PlanRewriteExhaustedReason;
pub(crate) const PLAN_REWRITE_EXHAUSTED_SSE: &str =
"结构化规划仍未满足要求(已达最大重写次数),已结束本轮;请调整需求后重试。";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum PlanRequirementSource {
None,
ConfigAlways,
WorkflowReflection,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PerCoordinatorInit {
pub reflection_default_max_rounds: usize,
pub final_plan_policy: FinalPlanRequirementMode,
pub plan_rewrite_max_attempts: usize,
pub final_plan_require_strict_workflow_node_coverage: bool,
pub final_plan_semantic_check_enabled: bool,
pub final_plan_semantic_check_max_non_readonly_tools: usize,
}
impl PerCoordinatorInit {
pub fn from_agent_config(cfg: &AgentConfig) -> Self {
Self {
reflection_default_max_rounds: cfg.per_plan_policy.reflection_default_max_rounds,
final_plan_policy: cfg.per_plan_policy.final_plan_requirement,
plan_rewrite_max_attempts: cfg.per_plan_policy.plan_rewrite_max_attempts,
final_plan_require_strict_workflow_node_coverage: cfg
.per_plan_policy
.final_plan_require_strict_workflow_node_coverage,
final_plan_semantic_check_enabled: cfg
.per_plan_policy
.final_plan_semantic_check_enabled,
final_plan_semantic_check_max_non_readonly_tools: cfg
.per_plan_policy
.final_plan_semantic_check_max_non_readonly_tools,
}
}
}
#[derive(Debug)]
pub enum AfterFinalAssistant {
StopTurn,
RequestPlanRewrite(Message),
StopTurnPlanRewriteExhausted { reason: PlanRewriteExhaustedReason },
StopTurnPendingPlanConsistencyLlm {
plan: plan_artifact::AgentReplyPlanV1,
tool_digest: Option<String>,
},
}
#[derive(Debug)]
pub struct PreparedWorkflowExecute {
pub patched_args: String,
pub execute: bool,
pub skipped_result: String,
pub reflection_inject: Option<Value>,
}
pub struct PerCoordinator {
reflection: WorkflowReflectionController,
final_plan_policy: FinalPlanRequirementMode,
plan_rewrite_max_attempts: usize,
final_plan_require_strict_workflow_node_coverage: bool,
final_plan_semantic_check_enabled: bool,
final_plan_semantic_check_max_non_readonly_tools: usize,
plan_requirement_source: PlanRequirementSource,
pub(crate) counters: per_turn_state::PerTurnCounters,
pub(crate) workflow_validate_cache: per_turn_state::WorkflowValidateLayerCache,
pub(crate) repeated_tool_failures: per_turn_state::RepeatedToolFailureMemo,
pub(crate) successful_run_commands: per_turn_state::SuccessfulRunCommandDedupeMemo,
}
mod coordinator_impl;
#[cfg(test)]
mod final_plan_gate_golden;
#[cfg(test)]
mod workflow_to_plan_requirement_golden;
#[cfg(test)]
impl PerCoordinator {
fn test_workflow_validate_layer_need(&mut self, messages: &[Message]) -> Option<usize> {
self.workflow_validate_layer_need(messages)
}
fn test_layer_cache_snapshot(&self) -> (Option<usize>, usize) {
self.workflow_validate_cache.snapshot()
}
}
#[cfg(test)]
mod tests;