use agent_contracts::backend::OperationBackend;
use agent_contracts::trace::TraceRecorderBuilder;
use agent_contracts::{
HookerRegistry, HookerRegistryBuilder, RuntimeView, ToolEventSink, ToolStateStore,
ToolStateStoreBuilder, TraceRecorder,
};
use agent_types::common::BuildError;
use agent_types::hook::{HookerDefaultMode, HookerRegistryConfig};
use agent_types::tool::ToolStateStoreConfig;
use hook::framework::HookerRegistryBuilderImpl;
use std::sync::Arc;
use tool::ToolStateStoreBuilderImpl;
use trace::TraceRecorderBuilderImpl;
use super::agent_context::MockAgentContext;
use super::interaction::CliInteractionHandle;
use super::tool_events::PrintStdoutToolEventSink;
#[derive(Debug, Clone)]
pub struct ToolCliTraceConfig {
pub backend: String,
pub db_path: Option<String>,
}
impl Default for ToolCliTraceConfig {
fn default() -> Self {
Self {
backend: "stdout".to_string(),
db_path: None,
}
}
}
pub struct ToolCliRuntime {
state_store: Box<dyn ToolStateStore>,
tool_events: PrintStdoutToolEventSink,
trace_recorder: Box<dyn TraceRecorder>,
agent_context: MockAgentContext,
interaction: CliInteractionHandle,
hookers: Box<dyn HookerRegistry>,
operation_backend: Arc<dyn OperationBackend>,
}
impl ToolCliRuntime {
pub async fn new(trace_config: ToolCliTraceConfig) -> Result<Self, BuildError> {
let hookers = HookerRegistryBuilderImpl::new()
.with_config(HookerRegistryConfig {
default: HookerDefaultMode::None,
..HookerRegistryConfig::default()
})
.build()?;
let state_store = ToolStateStoreBuilderImpl::new()
.with_config(ToolStateStoreConfig {
backend: serde_json::Value::String("stdout".to_string()),
retention: serde_json::Value::Null,
})
.build()?;
let trace_recorder = TraceRecorderBuilderImpl::default()
.from_json(serde_json::json!({
"storage_backend": trace_config.backend,
"db_path": trace_config.db_path,
"agent_id": "tool_cli",
}))?
.build()
.await?;
let workspace_root =
std::env::current_dir().map_err(|error| BuildError::DependencyError {
message: format!("failed to resolve current directory: {error}"),
})?;
let operation_backend = operation_backend::local_backend(workspace_root, None, None, None)
.map_err(|error| BuildError::InvalidConfig {
message: format!("failed to build local operation backend: {error}"),
})?;
Ok(Self {
state_store,
tool_events: PrintStdoutToolEventSink,
trace_recorder,
agent_context: MockAgentContext::new(),
interaction: CliInteractionHandle,
hookers,
operation_backend,
})
}
}
impl RuntimeView for ToolCliRuntime {
fn state_store(&self) -> &dyn ToolStateStore {
self.state_store.as_ref()
}
fn tool_events(&self) -> &dyn ToolEventSink {
&self.tool_events
}
fn trace_recorder(&self) -> &dyn TraceRecorder {
self.trace_recorder.as_ref()
}
fn agent_context(&self) -> &dyn agent_contracts::AgentContext {
&self.agent_context
}
fn interaction(&self) -> &dyn agent_contracts::InteractionHandle {
&self.interaction as &dyn agent_contracts::InteractionHandle
}
fn hookers(&self) -> &dyn HookerRegistry {
self.hookers.as_ref()
}
fn operation_backend(&self) -> Option<Arc<dyn OperationBackend>> {
Some(Arc::clone(&self.operation_backend))
}
}