Skip to main content

forge_agent/
runtime_integration.rs

1//! Runtime integration for Agent with ForgeRuntime.
2//!
3//! This module provides integration between the Agent and ForgeRuntime
4//! for coordinated file watching, caching, and automatic re-indexing.
5
6use crate::{Agent, AgentError, LoopResult};
7use forge_runtime::ForgeRuntime;
8use std::path::Path;
9
10impl Agent {
11    /// Creates agent with runtime for file watching and caching.
12    ///
13    /// This method initializes both the Agent and ForgeRuntime, allowing
14    /// the agent to leverage runtime services like query caching and
15    /// coordinated file watching.
16    ///
17    /// # Arguments
18    ///
19    /// * `codebase_path` - Path to the codebase
20    ///
21    /// # Returns
22    ///
23    /// Returns a tuple of (Agent, ForgeRuntime) on success.
24    ///
25    /// # Example
26    ///
27    /// ```no_run
28    /// use forge_agent::Agent;
29    ///
30    /// # #[tokio::main]
31    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
32    /// let (agent, mut runtime) = Agent::with_runtime("./project").await?;
33    ///
34    /// // Start file watching
35    /// runtime.watch().await?;
36    ///
37    /// // Run agent with runtime coordination
38    /// let result = agent.run_with_runtime(&mut runtime, "refactor function").await?;
39    /// # Ok(())
40    /// # }
41    /// ```
42    pub async fn with_runtime(
43        codebase_path: impl AsRef<Path>,
44    ) -> Result<(Self, ForgeRuntime), AgentError> {
45        let path = codebase_path.as_ref();
46
47        // Create runtime first
48        let runtime = ForgeRuntime::new(path)
49            .await
50            .map_err(|e| AgentError::ObservationFailed(format!("Failed to create runtime: {}", e)))?;
51
52        // Create agent with runtime's forge (shares the same graph store)
53        let agent = Agent::new(path).await?;
54
55        Ok((agent, runtime))
56    }
57
58    /// Runs agent loop with runtime coordination.
59    ///
60    /// This method coordinates with ForgeRuntime for optimal performance:
61    /// - Query cache access for faster graph operations
62    /// - Metrics collection for observability
63    ///
64    /// Note: For v0.4, watcher pause/resume is deferred to future versions.
65    /// The runtime provides cache access and metrics, but file watching
66    /// coordination is a future enhancement.
67    ///
68    /// # Arguments
69    ///
70    /// * `runtime` - Mutable reference to the ForgeRuntime
71    /// * `query` - The natural language query or request
72    ///
73    /// # Returns
74    ///
75    /// Returns `LoopResult` with transaction ID, modified files, and audit trail.
76    ///
77    /// # Example
78    ///
79    /// ```no_run
80    /// use forge_agent::Agent;
81    ///
82    /// # #[tokio::main]
83    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
84    /// let (agent, mut runtime) = Agent::with_runtime("./project").await?;
85    /// let result = agent.run_with_runtime(&mut runtime, "add error handling").await?;
86    /// # Ok(())
87    /// # }
88    /// ```
89    pub async fn run_with_runtime(
90        &self,
91        _runtime: &mut ForgeRuntime,
92        query: &str,
93    ) -> crate::Result<LoopResult> {
94        // For v0.4, we don't pause watcher (future enhancement)
95        // Just run the normal loop - runtime provides cache access and metrics
96
97        // TODO: Future version - Add watcher.pause() / watcher.resume()
98        // TODO: Future version - Utilize runtime cache for graph query optimization
99
100        self.run(query).await
101    }
102
103    /// Gets reference to runtime cache if available.
104    ///
105    /// This method provides access to the runtime's query cache for
106    /// optimization of repeated graph queries.
107    ///
108    /// # Returns
109    ///
110    /// Returns `None` - cache access requires runtime association (not yet implemented).
111    ///
112    /// Note: This is a placeholder for future functionality.
113    pub fn runtime_cache(&self) -> Option<()> {
114        // TODO: Return cache from associated runtime
115        // For v0.4, return None (cache access will be added in future version)
116        None
117    }
118
119    /// Gets runtime statistics if available.
120    ///
121    /// This method provides access to runtime metrics including cache size,
122    /// watch status, and reindex count.
123    ///
124    /// # Returns
125    ///
126    /// Returns `None` - stats access requires runtime association (not yet implemented).
127    ///
128    /// Note: This is a placeholder for future functionality.
129    pub fn runtime_stats(&self) -> Option<()> {
130        // TODO: Return stats from associated runtime
131        // For v0.4, return None
132        None
133    }
134}
135
136#[cfg(test)]
137mod tests {
138    use super::*;
139
140    #[tokio::test]
141    async fn test_agent_with_runtime_creation() {
142        let temp = tempfile::tempdir().unwrap();
143        let (agent, runtime) = Agent::with_runtime(temp.path()).await.unwrap();
144
145        // Verify both agent and runtime were created
146        assert_eq!(agent.codebase_path, temp.path());
147        assert_eq!(runtime.codebase_path(), temp.path());
148    }
149
150    #[tokio::test]
151    async fn test_agent_run_with_runtime() {
152        let temp = tempfile::tempdir().unwrap();
153        let (agent, mut runtime) = Agent::with_runtime(temp.path()).await.unwrap();
154
155        // Run agent with runtime
156        let result = agent.run_with_runtime(&mut runtime, "test query").await;
157
158        // Should complete (may fail on actual query processing, but infrastructure works)
159        assert!(result.is_ok() || result.is_err());
160    }
161
162    #[tokio::test]
163    async fn test_agent_runtime_stats_returns_none() {
164        let temp = tempfile::tempdir().unwrap();
165        let (agent, _runtime) = Agent::with_runtime(temp.path()).await.unwrap();
166
167        // For v0.4, runtime_stats returns None
168        assert!(agent.runtime_stats().is_none());
169    }
170
171    #[tokio::test]
172    async fn test_agent_runtime_cache_returns_none() {
173        let temp = tempfile::tempdir().unwrap();
174        let (agent, _runtime) = Agent::with_runtime(temp.path()).await.unwrap();
175
176        // For v0.4, runtime_cache returns None
177        assert!(agent.runtime_cache().is_none());
178    }
179}