use crate::assert_invariant;
#[cfg(feature = "rag")]
use crate::rag::RagSystem;
#[cfg(feature = "sandbox")]
use crate::sandbox::EnhancedSandbox;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::collections::HashMap;
use std::sync::Arc;
use std::time::Instant;
use tokio::sync::RwLock;
use uuid::Uuid;
pub mod error;
pub mod error_formatting;
pub use error::*;
pub use error_formatting::*;
pub mod mission;
pub use mission::*;
pub mod executor;
pub use executor::*;
pub mod audit;
pub use audit::*;
pub mod memory;
pub use memory::*;
pub mod agent;
pub use agent::*;
pub mod chain;
pub use chain::*;
pub mod llm;
pub use llm::*;
pub mod tools;
pub use tools::*;
#[cfg(feature = "tools")]
pub mod web_search_tools;
#[cfg(feature = "tools")]
pub mod document_loaders;
#[cfg(feature = "tools")]
pub use document_loaders::*;
#[cfg(feature = "rag")]
pub mod pinecone_vector_store;
#[cfg(feature = "rag")]
pub mod chroma_vector_store;
#[cfg(feature = "tools")]
pub mod python_interpreter;
#[cfg(feature = "tools")]
pub use python_interpreter::*;
#[cfg(feature = "tools")]
pub mod github_toolkit;
pub mod plugin;
pub use plugin::*;
pub mod features;
pub use features::*;
#[cfg(feature = "tools")]
pub mod http_client;
#[cfg(feature = "tools")]
pub use http_client::*;
pub mod config;
pub use config::*;
#[derive(Clone)]
pub struct RuntimeContext {
pub config: Arc<RwLock<Config>>,
pub audit: AuditSink, pub tool_registry: Arc<RwLock<ToolRegistry>>,
pub model_manager: Option<Arc<ModelManager>>,
pub sandbox: Arc<AgentSandbox>,
pub policy_engine: Arc<PolicyEngine>,
pub perf_collector: Arc<RwLock<PerfCollector>>,
pub plugin_manager: Arc<RwLock<PluginManager>>,
pub feature_detector: Arc<FeatureDetector>,
#[cfg(feature = "rag")]
pub rag_system: Option<Arc<RwLock<RagSystem>>>,
#[cfg(feature = "sandbox")]
pub enhanced_sandbox: Option<Arc<EnhancedSandbox>>,
}
impl RuntimeContext {
pub fn new() -> Self {
assert_invariant!(true, "RuntimeContext created", Some("core"));
Self {
config: Arc::new(RwLock::new(Config::default())),
audit: AuditSink::new(), tool_registry: Arc::new(RwLock::new(ToolRegistry::new())),
model_manager: None,
sandbox: Arc::new(AgentSandbox::new()),
policy_engine: Arc::new(PolicyEngine::new()),
perf_collector: Arc::new(RwLock::new(PerfCollector::new())),
plugin_manager: Arc::new(RwLock::new(PluginManager::new())),
feature_detector: Arc::new(FeatureDetector::new()),
#[cfg(feature = "rag")]
rag_system: None,
#[cfg(feature = "sandbox")]
enhanced_sandbox: None,
}
}
pub async fn audit_action(&self, agent_id: &str, action: &str, outcome: &str) {
let entry = AuditEntry {
id: Uuid::new_v4(),
timestamp: Utc::now(),
actor: agent_id.to_string(),
action: action.to_string(),
outcome: outcome.to_string(),
reason: None,
};
if let Err(e) = self.audit.log(entry).await {
tracing::error!(
"Failed to log audit entry: {}. Audit integrity may be compromised.",
e
);
}
}
pub async fn has_enterprise_feature(&self, feature: &str) -> bool {
if cfg!(feature = "enterprise") {
self.plugin_manager.read().await.has_feature(feature)
} else {
false
}
}
pub async fn get_enterprise_features(&self) -> Vec<String> {
if cfg!(feature = "enterprise") {
self.plugin_manager.read().await.enabled_features()
} else {
vec![]
}
}
pub async fn get_available_features(&self) -> Vec<String> {
#[allow(unused_mut)]
let mut features = vec![
"mission_execution".to_string(),
"safety_validation".to_string(),
"audit_logging".to_string(),
"policy_engine".to_string(),
];
#[cfg(feature = "llm")]
features.push("llm_integration".to_string());
#[cfg(feature = "tools")]
features.push("tool_system".to_string());
#[cfg(feature = "rag")]
features.push("rag_system".to_string());
#[cfg(feature = "sandbox")]
features.push("sandbox".to_string());
#[cfg(feature = "server")]
features.push("api_server".to_string());
features
}
pub async fn load_enterprise_plugins(&self) -> crate::core::error::Result<()> {
Ok(())
}
pub async fn check_feature_status(&self, feature: &str) -> FeatureStatus {
self.feature_detector
.is_feature_available(self, feature)
.await
}
pub async fn require_feature(&self, feature: &str) -> crate::core::error::Result<()> {
self.feature_detector.require_feature(self, feature).await
}
pub async fn get_feature_summary(&self) -> FeatureSummary {
self.feature_detector.get_feature_summary(self).await
}
pub async fn get_category_status(&self, category: &str) -> Vec<FeatureStatus> {
self.feature_detector
.get_category_status(self, category)
.await
}
pub async fn is_enterprise_complete(&self) -> bool {
self.get_feature_summary().await.is_enterprise_complete()
}
}
impl Default for RuntimeContext {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Config {
pub mission_timeout_seconds: u64,
pub max_parallel_steps: usize,
pub audit_enabled: bool,
pub network_policy: NetworkPolicy,
pub agent_id: String,
pub max_tool_calls: usize,
}
impl Default for Config {
fn default() -> Self {
Self {
mission_timeout_seconds: 300,
max_parallel_steps: 4,
audit_enabled: true,
network_policy: NetworkPolicy::Offline,
agent_id: "rustchain-agent".to_string(),
max_tool_calls: 100,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum NetworkPolicy {
Offline,
AllowList(Vec<String>),
}
#[derive(Clone)]
pub struct AuditSink {
entries: Arc<RwLock<Vec<AuditEntry>>>,
}
impl AuditSink {
pub fn new() -> Self {
Self {
entries: Arc::new(RwLock::new(Vec::new())),
}
}
pub async fn log(&self, entry: AuditEntry) -> Result<()> {
self.entries.write().await.push(entry);
Ok(())
}
pub async fn get_entries(&self) -> Vec<AuditEntry> {
self.entries.read().await.clone()
}
pub async fn get_chain_hash(&self) -> String {
let entries = self.entries.read().await;
if entries.is_empty() {
return "genesis".to_string();
}
let mut hasher = Sha256::new();
for entry in entries.iter() {
hasher.update(
format!(
"{}{}{}{}",
entry.timestamp.to_rfc3339(),
entry.actor,
entry.action,
entry.outcome
)
.as_bytes(),
);
}
format!("{:x}", hasher.finalize())
}
}
impl Default for AuditSink {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AuditEntry {
pub id: Uuid,
pub timestamp: DateTime<Utc>,
pub actor: String,
pub action: String,
pub outcome: String,
pub reason: Option<String>,
}
pub struct ToolRegistry {
tools: HashMap<String, Box<dyn Tool + Send + Sync>>,
}
impl Default for ToolRegistry {
fn default() -> Self {
Self::new()
}
}
impl ToolRegistry {
pub fn new() -> Self {
Self {
tools: HashMap::new(),
}
}
pub fn register(&mut self, name: String, tool: Box<dyn Tool + Send + Sync>) {
assert_invariant!(!name.is_empty(), "Tool name cannot be empty", Some("core"));
assert_invariant!(
tool.name() == name,
"Tool name must match provided name",
Some("core")
);
self.tools.insert(name, tool);
}
pub fn get(&self, name: &str) -> Option<&(dyn Tool + Send + Sync)> {
let tool = self.tools.get(name).map(|tool| tool.as_ref());
if let Some(t) = tool {
assert_invariant!(
t.name() == name,
"Retrieved tool name must match requested name",
Some("core")
);
}
tool
}
}
pub trait Tool {
fn name(&self) -> &str;
fn invoke(&self, args: serde_json::Value) -> anyhow::Result<serde_json::Value>;
}
#[derive(Debug, Clone)]
pub struct PerfMetric {
pub name: String,
pub duration_ms: u128,
}
#[derive(Default)]
pub struct PerfCollector {
active: HashMap<String, Instant>,
pub completed: Vec<PerfMetric>,
}
impl PerfCollector {
pub fn new() -> Self {
Self::default()
}
pub fn start(&mut self, name: &str) {
assert_invariant!(
!name.is_empty(),
"Performance metric name cannot be empty",
Some("core")
);
assert_invariant!(
!self.active.contains_key(name),
"Performance metric already started",
Some("core")
);
self.active.insert(name.to_string(), Instant::now());
}
pub fn end(&mut self, name: &str) {
assert_invariant!(
!name.is_empty(),
"Performance metric name cannot be empty",
Some("core")
);
if let Some(start) = self.active.remove(name) {
let duration = start.elapsed().as_millis();
self.completed.push(PerfMetric {
name: name.to_string(),
duration_ms: duration,
});
} else {
assert_invariant!(
false,
"Cannot end performance metric that was not started",
Some("core")
);
}
}
pub fn summary(&self) -> String {
self.completed
.iter()
.map(|m| format!("{}: {}ms", m.name, m.duration_ms))
.collect::<Vec<_>>()
.join("\n")
}
}
#[derive(Default)]
pub struct ModelManager {
#[cfg(feature = "llm")]
llm_manager: Option<crate::llm::LLMManager>,
}
impl ModelManager {
pub fn new() -> Self {
Self::default()
}
#[cfg(feature = "llm")]
pub fn with_llm_manager(mut self, manager: crate::llm::LLMManager) -> Self {
self.llm_manager = Some(manager);
self
}
#[cfg(not(feature = "llm"))]
pub async fn complete(
&self,
_request: serde_json::Value,
_provider: Option<&str>,
) -> anyhow::Result<serde_json::Value> {
Err(anyhow::anyhow!(
"LLM feature not enabled - rebuild with --features llm"
))
}
}
pub struct AgentSandbox {
allowed_paths: Vec<std::path::PathBuf>,
allowed_commands: Vec<String>,
}
impl Default for AgentSandbox {
fn default() -> Self {
let current_dir = std::env::current_dir().unwrap_or_else(|_| std::path::PathBuf::from("."));
Self {
allowed_paths: vec![current_dir],
allowed_commands: vec!["echo".to_string(), "cat".to_string(), "ls".to_string()],
}
}
}
impl AgentSandbox {
pub fn new() -> Self {
Self::default()
}
pub fn execute(&self, code: &str) -> std::result::Result<String, String> {
if let Some(path) = code.strip_prefix("create_file:") {
let path_buf = std::path::PathBuf::from(path);
if self.is_path_allowed(&path_buf) {
return Ok("allowed".to_string());
}
}
if self
.allowed_commands
.iter()
.any(|cmd| code.starts_with(cmd))
{
return Ok("allowed".to_string());
}
Err("Sandbox not implemented - failing closed".to_string())
}
fn is_path_allowed(&self, path: &std::path::Path) -> bool {
if path.is_relative() {
return true; }
self.allowed_paths
.iter()
.any(|allowed| path.starts_with(allowed))
}
}
pub struct PolicyEngine {
policies: Vec<String>,
}
impl Default for PolicyEngine {
fn default() -> Self {
Self::new()
}
}
impl PolicyEngine {
pub fn new() -> Self {
Self {
policies: Vec::new(),
}
}
pub fn validate(&self, action: &str) -> bool {
for policy in &self.policies {
if action.contains(policy) {
return false; }
}
true }
pub fn add_policy(&mut self, policy: String) {
assert_invariant!(!policy.is_empty(), "Policy cannot be empty", Some("core"));
self.policies.push(policy);
}
}