use std::path::Path;
use std::pin::Pin;
use std::sync::Arc;
use agent_client_protocol_schema::{ToolCallId, ToolCallUpdateFields};
use futures::Stream;
use futures::future::BoxFuture;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use tokio_util::sync::CancellationToken;
use crate::error::BoxError;
use crate::fs::FsBackend;
use crate::http::HttpClient;
use crate::session::EventEmitter;
use crate::shell::ShellBackend;
mod background_tasks;
mod goal_done;
mod skill;
mod spawn_agent;
pub use background_tasks::{CancelBackgroundTaskTool, InspectBackgroundTaskTool};
pub use goal_done::{GOAL_DONE_TOOL_NAME, GoalDoneTool};
pub use skill::{SkillEntry, SkillTool, SkillTriggers};
pub use spawn_agent::{SpawnAgentTool, SubagentProfile};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ToolSchema {
pub name: String,
pub description: String,
pub input_schema: serde_json::Value,
}
#[derive(Debug, Clone)]
pub struct ToolCallDescription {
pub fields: ToolCallUpdateFields,
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SafetyClass {
ReadOnly,
Mutating,
Destructive,
Network,
}
#[non_exhaustive]
#[derive(Debug)]
pub enum ToolEvent {
Progress(ToolCallUpdateFields),
Completed(ToolCallUpdateFields),
Failed(ToolError),
}
pub type ToolStream = Pin<Box<dyn Stream<Item = ToolEvent> + Send>>;
#[non_exhaustive]
pub struct ToolContext<'a> {
pub cwd: &'a Path,
pub cancel: CancellationToken,
pub fs: Arc<dyn FsBackend>,
pub shell: Arc<dyn ShellBackend>,
pub http: Arc<dyn HttpClient>,
pub current_model: &'a str,
pub current_provider: &'a str,
pub background: Option<crate::session::BackgroundTasks>,
pub subagent_bridge: Option<SubagentBridge>,
pub policy: Option<Arc<dyn crate::policy::SandboxPolicy>>,
pub goal: Option<Arc<crate::session::GoalState>>,
pub subagent_depth: u32,
}
#[derive(Clone)]
pub struct SubagentBridge {
pub parent_events: Arc<EventEmitter>,
pub parent_tool_call_id: ToolCallId,
}
impl<'a> ToolContext<'a> {
pub fn new(
cwd: &'a Path,
cancel: CancellationToken,
fs: Arc<dyn FsBackend>,
shell: Arc<dyn ShellBackend>,
http: Arc<dyn HttpClient>,
current_model: &'a str,
) -> Self {
Self {
cwd,
cancel,
fs,
shell,
http,
current_model,
current_provider: "",
background: None,
subagent_bridge: None,
policy: None,
goal: None,
subagent_depth: 0,
}
}
#[must_use]
pub fn with_current_provider(mut self, vendor: &'a str) -> Self {
self.current_provider = vendor;
self
}
#[must_use]
pub fn with_subagent_depth(mut self, depth: u32) -> Self {
self.subagent_depth = depth;
self
}
#[must_use]
pub fn with_policy(mut self, policy: Arc<dyn crate::policy::SandboxPolicy>) -> Self {
self.policy = Some(policy);
self
}
#[must_use]
pub fn with_background(mut self, background: crate::session::BackgroundTasks) -> Self {
self.background = Some(background);
self
}
#[must_use]
pub fn with_goal(mut self, goal: Arc<crate::session::GoalState>) -> Self {
self.goal = Some(goal);
self
}
#[must_use]
pub fn with_subagent_bridge(mut self, bridge: SubagentBridge) -> Self {
self.subagent_bridge = Some(bridge);
self
}
}
pub trait Tool: Send + Sync {
fn schema(&self) -> &ToolSchema;
fn safety_hint(&self, args: &serde_json::Value) -> SafetyClass;
fn describe<'a>(
&'a self,
args: &'a serde_json::Value,
ctx: ToolContext<'a>,
) -> BoxFuture<'a, ToolCallDescription>;
fn execute(&self, args: serde_json::Value, ctx: ToolContext<'_>) -> ToolStream;
}
#[non_exhaustive]
#[derive(Debug, Error)]
pub enum ToolError {
#[error("tool canceled")]
Canceled,
#[error("invalid tool arguments: {0}")]
InvalidArgs(#[source] BoxError),
#[error("tool execution failed: {0}")]
Execution(#[source] BoxError),
}