1use std::path::PathBuf;
17
18pub mod observe;
20
21pub mod policy;
23
24pub mod planner;
26
27pub mod mutate;
29
30pub mod verify;
32
33pub mod commit;
35
36pub mod r#loop;
38
39pub mod audit;
41
42pub mod workflow;
44
45#[derive(thiserror::Error, Debug)]
47pub enum AgentError {
48 #[error("Observation failed: {0}")]
50 ObservationFailed(String),
51
52 #[error("Planning failed: {0}")]
54 PlanningFailed(String),
55
56 #[error("Mutation failed: {0}")]
58 MutationFailed(String),
59
60 #[error("Verification failed: {0}")]
62 VerificationFailed(String),
63
64 #[error("Commit failed: {0}")]
66 CommitFailed(String),
67
68 #[error("Policy violation: {0}")]
70 PolicyViolation(String),
71
72 #[error("Forge error: {0}")]
74 ForgeError(#[from] forge_core::ForgeError),
75}
76
77pub type Result<T> = std::result::Result<T, AgentError>;
79
80pub use policy::{Policy, PolicyReport, PolicyValidator, PolicyViolation};
82
83pub use observe::Observation;
85
86pub use r#loop::{AgentLoop, AgentPhase, LoopResult};
88
89pub use audit::{AuditEvent, AuditLog};
91
92pub use workflow::{
94 Dependency, TaskContext, TaskError, TaskId, TaskResult, ValidationReport, Workflow,
95 WorkflowError, WorkflowExecutor, WorkflowResult, WorkflowTask, WorkflowValidator,
96};
97
98#[derive(Clone, Debug)]
100pub struct ConstrainedPlan {
101 pub observation: Observation,
103 pub policy_violations: Vec<policy::PolicyViolation>,
105}
106
107#[derive(Clone, Debug)]
109pub struct ExecutionPlan {
110 pub steps: Vec<planner::PlanStep>,
112 pub estimated_impact: planner::ImpactEstimate,
114 pub rollback_plan: Vec<planner::RollbackStep>,
116}
117
118#[derive(Clone, Debug)]
120pub struct MutationResult {
121 pub modified_files: Vec<PathBuf>,
123 pub diffs: Vec<String>,
125}
126
127#[derive(Clone, Debug)]
129pub struct VerificationResult {
130 pub passed: bool,
132 pub diagnostics: Vec<String>,
134}
135
136#[derive(Clone, Debug)]
138pub struct CommitResult {
139 pub transaction_id: String,
141 pub files_committed: Vec<PathBuf>,
143}
144
145pub struct Agent {
166 #[allow(dead_code)]
168 codebase_path: PathBuf,
169 forge: Option<forge_core::Forge>,
171}
172
173impl Agent {
174 pub async fn new(codebase_path: impl AsRef<std::path::Path>) -> Result<Self> {
180 let path = codebase_path.as_ref().to_path_buf();
181
182 let forge = forge_core::Forge::open(&path).await.ok();
184
185 Ok(Self {
186 codebase_path: path,
187 forge,
188 })
189 }
190
191 pub async fn observe(&self, query: &str) -> Result<Observation> {
197 let forge = self
198 .forge
199 .as_ref()
200 .ok_or_else(|| AgentError::ObservationFailed("Forge SDK not available".to_string()))?;
201
202 let observer = observe::Observer::new(forge.clone());
203 let obs = observer.gather(query).await?;
204
205 Ok(obs)
207 }
208
209 pub async fn constrain(
216 &self,
217 observation: Observation,
218 policies: Vec<policy::Policy>,
219 ) -> Result<ConstrainedPlan> {
220 let forge = self.forge.as_ref().ok_or_else(|| {
221 AgentError::ObservationFailed(
222 "Forge SDK not available for policy validation".to_string(),
223 )
224 })?;
225
226 let validator = policy::PolicyValidator::new(forge.clone());
228
229 let diff = policy::Diff {
232 file_path: std::path::PathBuf::from(""),
233 original: String::new(),
234 modified: String::new(),
235 changes: Vec::new(),
236 };
237
238 let report = validator.validate(&diff, &policies).await?;
240
241 Ok(ConstrainedPlan {
242 observation,
243 policy_violations: report.violations,
244 })
245 }
246
247 pub async fn plan(&self, constrained: ConstrainedPlan) -> Result<ExecutionPlan> {
249 let planner_instance = planner::Planner::new();
251
252 let obs = observe::Observation {
254 query: constrained.observation.query.clone(),
255 symbols: vec![],
256 };
257
258 let steps = planner_instance.generate_steps(&obs).await?;
260
261 let impact = planner_instance.estimate_impact(&steps).await?;
263
264 let conflicts = planner_instance.detect_conflicts(&steps)?;
266
267 if !conflicts.is_empty() {
268 return Err(AgentError::PlanningFailed(format!(
269 "Found {} conflicts in plan",
270 conflicts.len()
271 )));
272 }
273
274 let mut ordered_steps = steps;
276 planner_instance.order_steps(&mut ordered_steps)?;
277
278 let rollback = planner_instance.generate_rollback(&ordered_steps);
280
281 Ok(ExecutionPlan {
282 steps: ordered_steps,
283 estimated_impact: planner::ImpactEstimate {
284 affected_files: impact.affected_files,
285 complexity: impact.complexity,
286 },
287 rollback_plan: rollback,
288 })
289 }
290
291 pub async fn mutate(&self, plan: ExecutionPlan) -> Result<MutationResult> {
293 self.forge.as_ref().ok_or_else(|| AgentError::MutationFailed("Forge SDK not available".to_string()))?;
295
296 let mut mutator = mutate::Mutator::new();
297 mutator.begin_transaction().await?;
298
299 for step in &plan.steps {
300 mutator.apply_step(step).await?;
301 }
302
303 Ok(MutationResult {
304 modified_files: vec![],
305 diffs: vec!["Transaction completed".to_string()],
306 })
307 }
308
309 pub async fn verify(&self, _result: MutationResult) -> Result<VerificationResult> {
311 let verifier = verify::Verifier::new();
312 let report = verifier.verify(&self.codebase_path).await?;
313
314 Ok(VerificationResult {
315 passed: report.passed,
316 diagnostics: report
317 .diagnostics
318 .iter()
319 .map(|d| d.message.clone())
320 .collect(),
321 })
322 }
323
324 pub async fn commit(&self, result: VerificationResult) -> Result<CommitResult> {
326 let committer = commit::Committer::new();
327 let files: Vec<std::path::PathBuf> = result
328 .diagnostics
329 .iter()
330 .filter_map(|d| {
331 d.split(':')
334 .next()
335 .map(|s| std::path::PathBuf::from(s.trim()))
336 })
337 .collect();
338
339 let commit_report = committer.finalize(&self.codebase_path, &files).await?;
340
341 Ok(CommitResult {
342 transaction_id: commit_report.transaction_id,
343 files_committed: commit_report.files_committed,
344 })
345 }
346
347 pub async fn run(&self, query: &str) -> Result<LoopResult> {
374 let forge = self
375 .forge
376 .as_ref()
377 .ok_or_else(|| AgentError::ObservationFailed("Forge SDK not available".to_string()))?;
378
379 let mut agent_loop = r#loop::AgentLoop::new(std::sync::Arc::new(forge.clone()));
381
382 agent_loop.run(query).await
383 }
384}
385
386pub mod transaction;
388
389pub use transaction::{FileSnapshot, Transaction, TransactionState};
391
392pub mod runtime_integration;
394
395pub use forge_runtime::{ForgeRuntime, RuntimeConfig, RuntimeStats};
397
398#[cfg(test)]
399mod tests {
400 use super::*;
401
402 #[tokio::test]
403 async fn test_agent_creation() {
404 let temp = tempfile::tempdir().unwrap();
405 let agent = Agent::new(temp.path()).await.unwrap();
406
407 assert_eq!(agent.codebase_path, temp.path());
408 }
409
410 #[tokio::test]
411 async fn test_agent_with_runtime() {
412 let temp = tempfile::tempdir().unwrap();
413 let (_agent, mut runtime) = Agent::with_runtime(temp.path()).await.unwrap();
414
415 assert_eq!(runtime.codebase_path(), temp.path());
417
418 let result = _agent.run("test query").await;
420
421 assert!(result.is_ok() || result.is_err());
423 }
424
425 #[tokio::test]
426 async fn test_agent_runtime_stats() {
427 let temp = tempfile::tempdir().unwrap();
428 let (_agent, runtime) = Agent::with_runtime(temp.path()).await.unwrap();
429
430 let stats = runtime.stats();
431 assert!(!stats.watch_active); }
433
434 #[tokio::test]
435 async fn test_agent_backward_compatibility() {
436 let temp = tempfile::tempdir().unwrap();
438 let agent = Agent::new(temp.path()).await.unwrap();
439
440 assert_eq!(agent.codebase_path, temp.path());
442
443 assert!(agent.runtime_cache().is_none());
445 assert!(agent.runtime_stats().is_none());
446 }
447}