1pub mod accumulator;
94pub mod agentic;
95pub mod bash_completion;
96pub mod context;
97pub mod execution_contract;
98pub mod executor;
99pub mod input_guard;
100mod output_cap_observation;
101pub mod pending_operation;
102pub mod registry;
103pub mod result_handler;
104pub mod smart_code_review;
105mod suspension;
106pub mod tool_runtime;
107pub mod types;
108
109pub use accumulator::{
110 finalize_tool_calls, update_partial_tool_call, update_partial_tool_call_indexed,
111 PartialToolCall, ToolCallAccumulator,
112};
113pub use agentic::{
114 convert_from_standard_result, convert_to_standard_result, AgenticContext, AgenticTool,
115 AgenticToolExecutor, AgenticToolResult, Interaction, InteractionRole, ToolGoal,
116};
117pub use bash_completion::{BashCompletionInfo, BashCompletionSink};
118pub use context::{
119 with_executing_session_caller, ExecutingSupervisorObservation, ToolExecutionContext,
120 ToolExecutionSessionFlags,
121};
122pub use execution_contract::{
123 retained_tool_execution_for, retained_tool_execution_for_call, retained_tool_owner_lookup,
124 with_retained_tool_execution, RetainedToolExecution, FRAMEWORK_ROOT_SPAWN_CONTRACT_V1,
125};
126mod framework_session;
127pub use executor::{execute_tool_call, execute_tool_call_with_context, ToolError, ToolExecutor};
128pub use framework_session::{
129 FrameworkSessionToolGuard, FrameworkSessionToolKind, HostFrameworkSessionTool,
130};
131pub use output_cap_observation::{observed_tool_output_cap, scope_tool_output_cap};
132pub use pending_operation::{PendingToolOperation, PENDING_TOOL_OPERATION_METADATA_KEY};
133pub use registry::{
134 global_registry, normalize_tool_name, RegistryError, SharedTool, Tool, ToolRegistry,
135};
136pub use result_handler::{
137 execute_sub_actions, handle_tool_result_with_agentic_support,
138 handle_tool_result_with_agentic_support_and_persistence, is_waiting_for_children_control,
139 parse_tool_args, parse_tool_args_best_effort, send_clarification_request,
140 tool_result_requires_suspension, tool_start_arguments_for_display, try_parse_agentic_result,
141 ToolHandlingOutcome, MAX_SUB_ACTIONS,
142};
143pub use smart_code_review::SmartCodeReviewTool;
144pub use suspension::{
145 capture_suspension_restriction, current_tool_suspension_scope, observe_tool_suspension,
146 suspension_allowed, suspension_running_violated, with_suspension_disabled,
147 with_suspension_disabled_observed, SuspensionRestriction, ToolSuspensionObservation,
148};
149pub use tool_runtime::{
150 AsyncToolCompletionInfo, AsyncToolCompletionSink, AsyncWaitKind, RunningCompletion,
151 RunningHandle, ToolClass, ToolCtx, ToolOutcome, ToolResultFuture,
152};
153pub use types::{FunctionCall, FunctionSchema, ToolCall, ToolResult, ToolResultImage, ToolSchema};
154
155#[derive(Debug, Clone, Copy, PartialEq, Eq)]
157pub enum ToolMutability {
158 ReadOnly,
159 Mutating,
160}
161
162const READ_ONLY_TOOLS: &[&str] = &[
164 "Read",
165 "GetFileInfo",
166 "Glob",
167 "Grep",
168 "WebFetch",
169 "WebSearch",
170 "ViewImage",
171 "Workspace",
172 "BashOutput",
173 "session_note",
174 "memory_note",
175 "session_history",
176 "session_history_current",
177 "bamboo_diagnostics",
178 "recall",
179 "session_inspector",
180 "archive_context",
181 "compact_context",
182 "Sleep",
183 "update_goal",
187 "notify",
197];
198
199pub fn classify_tool(tool_name: &str) -> ToolMutability {
201 if READ_ONLY_TOOLS
202 .iter()
203 .any(|&t| t.eq_ignore_ascii_case(tool_name))
204 {
205 ToolMutability::ReadOnly
206 } else {
207 ToolMutability::Mutating
208 }
209}
210
211pub fn plan_mode_allows_tool(tool_name: &str) -> bool {
215 classify_tool(tool_name) == ToolMutability::ReadOnly
216 || [
217 "EnterPlanMode",
218 "ExitPlanMode",
219 "request_permissions",
220 "archive_context",
221 "compact_context",
222 "session_message",
225 ]
226 .iter()
227 .any(|allowed| allowed.eq_ignore_ascii_case(tool_name))
228}
229
230#[cfg(test)]
231mod tests {
232 use super::*;
233
234 #[test]
235 fn diagnostics_is_read_only_in_shared_plan_gate() {
236 assert_eq!(
237 classify_tool("bamboo_diagnostics"),
238 ToolMutability::ReadOnly
239 );
240 assert!(plan_mode_allows_tool("bamboo_diagnostics"));
241 assert!(plan_mode_allows_tool("BAMBOO_DIAGNOSTICS"));
242 assert!(!plan_mode_allows_tool("bamboo_diagnostics_repair"));
243 }
244
245 #[test]
246 fn classify_compact_context_as_read_only() {
247 assert_eq!(classify_tool("compact_context"), ToolMutability::ReadOnly);
248 }
249
250 #[test]
251 fn classify_archive_context_as_read_only() {
252 assert_eq!(classify_tool("archive_context"), ToolMutability::ReadOnly);
253 assert!(plan_mode_allows_tool("ARCHIVE_CONTEXT"));
254 }
255
256 #[test]
257 fn classify_compact_context_case_insensitive() {
258 assert_eq!(classify_tool("Compact_Context"), ToolMutability::ReadOnly);
259 assert_eq!(classify_tool("COMPACT_CONTEXT"), ToolMutability::ReadOnly);
260 }
261
262 #[test]
263 fn classify_write_as_mutating() {
264 assert_eq!(classify_tool("Write"), ToolMutability::Mutating);
265 }
266
267 #[test]
268 fn session_message_plan_entry_keeps_mutating_name_classification() {
269 assert!(plan_mode_allows_tool("session_message"));
270 assert_eq!(classify_tool("session_message"), ToolMutability::Mutating);
271 assert!(!plan_mode_allows_tool("session_control"));
272 }
273
274 #[test]
275 fn classify_unknown_as_mutating() {
276 assert_eq!(
277 classify_tool("totally_unknown_tool"),
278 ToolMutability::Mutating
279 );
280 }
281
282 #[test]
283 fn classify_all_read_only_tools() {
284 for name in READ_ONLY_TOOLS {
285 assert_eq!(
286 classify_tool(name),
287 ToolMutability::ReadOnly,
288 "{name} should be classified as read-only"
289 );
290 }
291 }
292}