1use std::time::Duration;
4
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
9#[serde(rename_all = "snake_case")]
10#[non_exhaustive]
11pub enum ConcurrencyMode {
12 ReadOnly,
14 #[default]
16 Concurrent,
17 Exclusive,
19}
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
23#[serde(rename_all = "snake_case")]
24#[non_exhaustive]
25pub enum Destructiveness {
26 #[default]
28 None,
29 Reversible,
31 Irreversible,
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
37#[serde(rename_all = "snake_case")]
38#[non_exhaustive]
39pub enum InterruptBehavior {
40 #[default]
42 Cancel,
43 WaitComplete,
45}
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
49#[serde(rename_all = "snake_case")]
50#[non_exhaustive]
51pub enum CapabilityFlag {
52 Read,
54 Write,
56 Execute,
58 Network,
60 Spawn,
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize)]
66#[non_exhaustive]
67pub struct ToolMetadata {
68 pub concurrency: ConcurrencyMode,
70 pub destructiveness: Destructiveness,
72 pub interrupt: InterruptBehavior,
74 pub timeout: Option<Duration>,
76 pub capabilities: Vec<CapabilityFlag>,
78 #[serde(default, skip_serializing_if = "Option::is_none")]
80 pub max_concurrency: Option<usize>,
81}
82
83impl Default for ToolMetadata {
84 fn default() -> Self {
85 Self {
86 concurrency: ConcurrencyMode::Concurrent,
87 destructiveness: Destructiveness::None,
88 interrupt: InterruptBehavior::Cancel,
89 timeout: None,
90 capabilities: Vec::new(),
91 max_concurrency: None,
92 }
93 }
94}
95
96impl ToolMetadata {
97 #[must_use]
99 pub fn read_only() -> Self {
100 Self {
101 concurrency: ConcurrencyMode::ReadOnly,
102 capabilities: vec![CapabilityFlag::Read],
103 ..Self::default()
104 }
105 }
106
107 #[must_use]
109 pub fn exclusive_write() -> Self {
110 Self {
111 concurrency: ConcurrencyMode::Exclusive,
112 destructiveness: Destructiveness::Reversible,
113 capabilities: vec![CapabilityFlag::Write],
114 ..Self::default()
115 }
116 }
117
118 #[must_use]
120 pub fn spawn() -> Self {
121 Self {
122 concurrency: ConcurrencyMode::Concurrent,
123 destructiveness: Destructiveness::None,
124 capabilities: vec![CapabilityFlag::Spawn],
125 ..Self::default()
126 }
127 }
128
129 #[must_use]
131 pub fn shell_execute(timeout: Duration) -> Self {
132 Self {
133 concurrency: ConcurrencyMode::Exclusive,
134 destructiveness: Destructiveness::Reversible,
135 interrupt: InterruptBehavior::Cancel,
136 timeout: Some(timeout),
137 capabilities: vec![CapabilityFlag::Execute, CapabilityFlag::Write],
138 max_concurrency: Some(1),
139 }
140 }
141
142 #[must_use]
144 pub const fn with_max_concurrency(mut self, n: Option<usize>) -> Self {
145 self.max_concurrency = n;
146 self
147 }
148
149 #[must_use]
151 pub fn allowed_in_read_only(&self) -> bool {
152 !self.capabilities.iter().any(|c| {
153 matches!(
154 c,
155 CapabilityFlag::Write | CapabilityFlag::Execute | CapabilityFlag::Spawn
156 )
157 }) && self.destructiveness == Destructiveness::None
158 && self.concurrency != ConcurrencyMode::Exclusive
159 }
160}