pub mod accumulator;
pub mod agentic;
pub mod bash_completion;
pub mod context;
pub mod executor;
pub mod input_guard;
mod output_cap_observation;
pub mod registry;
pub mod result_handler;
pub mod smart_code_review;
mod suspension;
pub mod tool_runtime;
pub mod types;
pub use accumulator::{
finalize_tool_calls, update_partial_tool_call, update_partial_tool_call_indexed,
PartialToolCall, ToolCallAccumulator,
};
pub use agentic::{
convert_from_standard_result, convert_to_standard_result, AgenticContext, AgenticTool,
AgenticToolExecutor, AgenticToolResult, Interaction, InteractionRole, ToolGoal,
};
pub use bash_completion::{BashCompletionInfo, BashCompletionSink};
pub use context::{
ExecutingSupervisorObservation, ToolExecutionContext, ToolExecutionSessionFlags,
};
pub use executor::{execute_tool_call, execute_tool_call_with_context, ToolError, ToolExecutor};
pub use output_cap_observation::{observed_tool_output_cap, scope_tool_output_cap};
pub use registry::{
global_registry, normalize_tool_name, RegistryError, SharedTool, Tool, ToolRegistry,
};
pub use result_handler::{
execute_sub_actions, handle_tool_result_with_agentic_support,
handle_tool_result_with_agentic_support_and_persistence, is_waiting_for_children_control,
parse_tool_args, parse_tool_args_best_effort, send_clarification_request,
tool_result_requires_suspension, tool_start_arguments_for_display, try_parse_agentic_result,
ToolHandlingOutcome, MAX_SUB_ACTIONS,
};
pub use smart_code_review::SmartCodeReviewTool;
pub use suspension::{
capture_suspension_restriction, current_tool_suspension_scope, observe_tool_suspension,
suspension_allowed, suspension_running_violated, with_suspension_disabled,
with_suspension_disabled_observed, SuspensionRestriction, ToolSuspensionObservation,
};
pub use tool_runtime::{
AsyncToolCompletionInfo, AsyncToolCompletionSink, AsyncWaitKind, RunningCompletion,
RunningHandle, ToolClass, ToolCtx, ToolOutcome, ToolResultFuture,
};
pub use types::{FunctionCall, FunctionSchema, ToolCall, ToolResult, ToolResultImage, ToolSchema};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ToolMutability {
ReadOnly,
Mutating,
}
const READ_ONLY_TOOLS: &[&str] = &[
"Read",
"GetFileInfo",
"Glob",
"Grep",
"WebFetch",
"WebSearch",
"ViewImage",
"Workspace",
"BashOutput",
"session_note",
"memory_note",
"session_history",
"session_history_current",
"bamboo_diagnostics",
"recall",
"session_inspector",
"archive_context",
"compact_context",
"Sleep",
"update_goal",
"notify",
];
pub fn classify_tool(tool_name: &str) -> ToolMutability {
if READ_ONLY_TOOLS
.iter()
.any(|&t| t.eq_ignore_ascii_case(tool_name))
{
ToolMutability::ReadOnly
} else {
ToolMutability::Mutating
}
}
pub fn plan_mode_allows_tool(tool_name: &str) -> bool {
classify_tool(tool_name) == ToolMutability::ReadOnly
|| [
"EnterPlanMode",
"ExitPlanMode",
"request_permissions",
"archive_context",
"compact_context",
"session_message",
]
.iter()
.any(|allowed| allowed.eq_ignore_ascii_case(tool_name))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn diagnostics_is_read_only_in_shared_plan_gate() {
assert_eq!(
classify_tool("bamboo_diagnostics"),
ToolMutability::ReadOnly
);
assert!(plan_mode_allows_tool("bamboo_diagnostics"));
assert!(plan_mode_allows_tool("BAMBOO_DIAGNOSTICS"));
assert!(!plan_mode_allows_tool("bamboo_diagnostics_repair"));
}
#[test]
fn classify_compact_context_as_read_only() {
assert_eq!(classify_tool("compact_context"), ToolMutability::ReadOnly);
}
#[test]
fn classify_archive_context_as_read_only() {
assert_eq!(classify_tool("archive_context"), ToolMutability::ReadOnly);
assert!(plan_mode_allows_tool("ARCHIVE_CONTEXT"));
}
#[test]
fn classify_compact_context_case_insensitive() {
assert_eq!(classify_tool("Compact_Context"), ToolMutability::ReadOnly);
assert_eq!(classify_tool("COMPACT_CONTEXT"), ToolMutability::ReadOnly);
}
#[test]
fn classify_write_as_mutating() {
assert_eq!(classify_tool("Write"), ToolMutability::Mutating);
}
#[test]
fn session_message_plan_entry_keeps_mutating_name_classification() {
assert!(plan_mode_allows_tool("session_message"));
assert_eq!(classify_tool("session_message"), ToolMutability::Mutating);
assert!(!plan_mode_allows_tool("session_control"));
}
#[test]
fn classify_unknown_as_mutating() {
assert_eq!(
classify_tool("totally_unknown_tool"),
ToolMutability::Mutating
);
}
#[test]
fn classify_all_read_only_tools() {
for name in READ_ONLY_TOOLS {
assert_eq!(
classify_tool(name),
ToolMutability::ReadOnly,
"{name} should be classified as read-only"
);
}
}
}