use std::{collections::HashMap, sync::Arc};
use async_trait::async_trait;
use tokio::sync::RwLock;
use crate::{
budget::IterationBudget,
config::AgentConfig,
error::{AgentError, ToolError},
memory::MemoryStore,
types::{ToolResult, ToolSchema},
};
#[derive(Debug, Default, Clone)]
pub struct SkillPermissions(pub HashMap<String, bool>);
impl SkillPermissions {
pub fn merge(&mut self, other: &HashMap<String, bool>) {
for (tool, allowed) in other {
let entry = self.0.entry(tool.clone()).or_insert(false);
if *allowed {
*entry = true;
}
}
}
pub fn check(&self, tool_name: &str) -> Option<bool> {
self.0.get(tool_name).copied()
}
}
#[async_trait]
pub trait Tool: Send + Sync + 'static {
fn name(&self) -> &str;
fn description(&self) -> &str;
fn schema(&self) -> serde_json::Value;
fn toolset(&self) -> &str;
fn is_destructive(&self) -> bool {
false
}
fn is_destructive_for(&self, _params: &serde_json::Value) -> bool {
self.is_destructive()
}
fn bypass_dispatch_timeout(&self) -> bool {
false
}
async fn execute(
&self,
params: serde_json::Value,
ctx: &ToolContext,
) -> Result<ToolResult, ToolError>;
fn to_schema(&self) -> ToolSchema {
ToolSchema {
name: self.name().to_string(),
description: self.description().to_string(),
parameters: self.schema(),
}
}
}
#[async_trait]
pub trait SubAgentRunner: Send + Sync + 'static {
async fn run_task(&self, task: &str, session_id: &str) -> Result<String, AgentError>;
}
pub struct ToolContext {
pub session_id: String,
pub agent_id: String,
pub iteration: u32,
pub budget: Arc<IterationBudget>,
pub memory: Arc<dyn MemoryStore>,
pub config: Arc<AgentConfig>,
pub approver: Arc<dyn CommandApprover>,
pub sub_agent: Option<Arc<dyn SubAgentRunner>>,
pub skill_permissions: Arc<RwLock<SkillPermissions>>,
pub required_tools: Arc<RwLock<Vec<String>>>,
}
#[async_trait]
pub trait CommandApprover: Send + Sync + 'static {
async fn approve(&self, tool_name: &str, params: &str) -> ApprovalDecision;
}
#[derive(Debug, Clone, PartialEq)]
pub enum ApprovalDecision {
Approved,
ApprovedAlways,
Denied,
Yolo,
}