agent_orchestrator/prehook/
mod.rs1mod cel;
2mod context;
3pub mod finalize;
5
6#[cfg(test)]
7mod tests;
8
9use crate::config::{StepHookEngine, StepPrehookConfig, WorkflowFinalizeRule};
10use anyhow::Result;
11use cel_interpreter::Program;
12
13pub use cel::{
15 evaluate_convergence_expression, evaluate_finalize_rule_expression,
16 evaluate_step_prehook_expression, evaluate_webhook_filter,
17};
18pub use finalize::{
19 emit_item_finalize_event, emit_step_prehook_event, evaluate_step_prehook,
20 resolve_workflow_finalize_outcome,
21};
22
23pub fn validate_agent_command_rules(
25 agent_id: &str,
26 rules: &[crate::config::AgentCommandRule],
27) -> Result<()> {
28 for (i, rule) in rules.iter().enumerate() {
29 let expression = rule.when.trim();
30 if expression.is_empty() {
31 anyhow::bail!(
32 "agent '{}' command_rules[{}].when cannot be empty",
33 agent_id,
34 i
35 );
36 }
37 let compiled = std::panic::catch_unwind(|| Program::compile(expression)).map_err(|_| {
38 anyhow::anyhow!(
39 "agent '{}' command_rules[{}].when caused CEL parser panic",
40 agent_id,
41 i
42 )
43 })?;
44 compiled.map_err(|err| {
45 anyhow::anyhow!(
46 "agent '{}' command_rules[{}].when is invalid CEL: {}",
47 agent_id,
48 i,
49 err
50 )
51 })?;
52 if !rule.command.contains("{prompt}") {
53 anyhow::bail!(
54 "agent '{}' command_rules[{}].command must contain {{prompt}} placeholder",
55 agent_id,
56 i
57 );
58 }
59 }
60 Ok(())
61}
62
63pub fn validate_step_prehook(
65 prehook: &StepPrehookConfig,
66 workflow_id: &str,
67 step_type: &str,
68) -> Result<()> {
69 let expression = prehook.when.trim();
70 if expression.is_empty() {
71 anyhow::bail!(
72 "workflow '{}' step '{}' prehook.when cannot be empty",
73 workflow_id,
74 step_type
75 );
76 }
77 match prehook.engine {
78 StepHookEngine::Cel => {
79 let compiled =
80 std::panic::catch_unwind(|| Program::compile(expression)).map_err(|_| {
81 anyhow::anyhow!(
82 "workflow '{}' step '{}' prehook.when caused CEL parser panic",
83 workflow_id,
84 step_type
85 )
86 })?;
87 compiled.map_err(|err| {
88 anyhow::anyhow!(
89 "workflow '{}' step '{}' prehook.when is invalid CEL: {}",
90 workflow_id,
91 step_type,
92 err
93 )
94 })?;
95 }
96 }
97 Ok(())
98}
99
100pub fn validate_workflow_finalize_rule(
102 rule: &WorkflowFinalizeRule,
103 workflow_id: &str,
104) -> Result<()> {
105 if rule.id.trim().is_empty() {
106 anyhow::bail!("workflow '{}' has finalize rule with empty id", workflow_id);
107 }
108 if rule.status.trim().is_empty() {
109 anyhow::bail!(
110 "workflow '{}' finalize rule '{}' has empty status",
111 workflow_id,
112 rule.id
113 );
114 }
115 let expression = rule.when.trim();
116 if expression.is_empty() {
117 anyhow::bail!(
118 "workflow '{}' finalize rule '{}' has empty when",
119 workflow_id,
120 rule.id
121 );
122 }
123 match rule.engine {
124 StepHookEngine::Cel => {
125 let compiled =
126 std::panic::catch_unwind(|| Program::compile(expression)).map_err(|_| {
127 anyhow::anyhow!(
128 "workflow '{}' finalize rule '{}' caused CEL parser panic",
129 workflow_id,
130 rule.id
131 )
132 })?;
133 compiled.map_err(|err| {
134 anyhow::anyhow!(
135 "workflow '{}' finalize rule '{}' invalid CEL: {}",
136 workflow_id,
137 rule.id,
138 err
139 )
140 })?;
141 }
142 }
143 Ok(())
144}