Skip to main content

agent_sdk/
primitive_tools.rs

1//! Primitive tools that work with the Environment abstraction.
2//!
3//! These tools provide basic file and command operations:
4//! - `ReadTool` - Read file contents
5//! - `WriteTool` - Write/create files
6//! - `EditTool` - Edit existing files with string replacement
7//! - `GlobTool` - Find files by pattern
8//! - `GrepTool` - Search file contents
9//! - `BashTool` - Execute shell commands
10//!
11//! All tools respect `AgentCapabilities` for security.
12
13mod bash;
14mod edit;
15mod glob;
16mod grep;
17mod read;
18mod write;
19
20pub use bash::BashTool;
21pub use edit::EditTool;
22pub use glob::GlobTool;
23pub use grep::GrepTool;
24pub use read::ReadTool;
25pub use write::WriteTool;
26
27use crate::{AgentCapabilities, Environment};
28use serde::Deserialize;
29use serde::de::{self, Deserializer};
30use std::fmt::Display;
31use std::str::FromStr;
32use std::sync::Arc;
33
34/// Context for primitive tools that need environment access
35pub struct PrimitiveToolContext<E: Environment> {
36    pub environment: Arc<E>,
37    pub capabilities: AgentCapabilities,
38}
39
40impl<E: Environment> PrimitiveToolContext<E> {
41    #[must_use]
42    pub const fn new(environment: Arc<E>, capabilities: AgentCapabilities) -> Self {
43        Self {
44            environment,
45            capabilities,
46        }
47    }
48}
49
50impl<E: Environment> Clone for PrimitiveToolContext<E> {
51    fn clone(&self) -> Self {
52        Self {
53            environment: Arc::clone(&self.environment),
54            capabilities: self.capabilities.clone(),
55        }
56    }
57}
58
59#[derive(Deserialize)]
60#[serde(untagged)]
61enum StringOrU64 {
62    Number(u64),
63    String(String),
64}
65
66#[derive(Deserialize)]
67#[serde(untagged)]
68enum StringOrUsize {
69    Number(usize),
70    String(String),
71}
72
73fn parse_numeric_string<T>(value: &str) -> Result<T, String>
74where
75    T: FromStr,
76    T::Err: Display,
77{
78    value
79        .trim()
80        .parse::<T>()
81        .map_err(|error| format!("invalid numeric string '{value}': {error}"))
82}
83
84pub(super) fn deserialize_optional_u64_from_string_or_int<'de, D>(
85    deserializer: D,
86) -> Result<Option<u64>, D::Error>
87where
88    D: Deserializer<'de>,
89{
90    match Option::<StringOrU64>::deserialize(deserializer)? {
91        None => Ok(None),
92        Some(StringOrU64::Number(value)) => Ok(Some(value)),
93        Some(StringOrU64::String(value)) => parse_numeric_string(&value)
94            .map(Some)
95            .map_err(de::Error::custom),
96    }
97}
98
99pub(super) fn deserialize_optional_usize_from_string_or_int<'de, D>(
100    deserializer: D,
101) -> Result<Option<usize>, D::Error>
102where
103    D: Deserializer<'de>,
104{
105    match Option::<StringOrUsize>::deserialize(deserializer)? {
106        None => Ok(None),
107        Some(StringOrUsize::Number(value)) => Ok(Some(value)),
108        Some(StringOrUsize::String(value)) => parse_numeric_string(&value)
109            .map(Some)
110            .map_err(de::Error::custom),
111    }
112}