use crate::error::AppError;
use bamboo_agent_core::tools::{FunctionCall, ToolCall};
use bamboo_domain::tool_names::resolve_alias;
use bamboo_tools::normalize_tool_ref;
use serde_json::Value;
use super::models::ToolParameter;
pub(super) fn canonical_tool_name_or_error(tool_name: &str) -> Result<String, AppError> {
let normalized = normalize_tool_ref(tool_name)
.ok_or_else(|| AppError::ToolNotFound(tool_name.to_string()))?;
Ok(resolve_alias(&normalized)
.unwrap_or(normalized.as_str())
.to_string())
}
pub(super) fn trimmed_session_id(session_id: Option<&str>) -> Option<&str> {
session_id.map(str::trim).filter(|value| !value.is_empty())
}
pub(super) fn validate_session_context_requirement(
tool_name: &str,
session_id: Option<&str>,
) -> Result<(), AppError> {
if requires_session_context(tool_name) && session_id.is_none() {
return Err(AppError::BadRequest(format!(
"Tool '{}' requires session_id for safe execution",
tool_name
)));
}
Ok(())
}
pub(super) fn parse_arguments(parameters: Vec<ToolParameter>) -> serde_json::Map<String, Value> {
let mut args = serde_json::Map::new();
for param in parameters {
let parsed = serde_json::from_str(¶m.value).unwrap_or(Value::String(param.value));
args.insert(param.name, parsed);
}
args
}
pub(super) fn build_tool_call(
canonical_tool_name: String,
args: serde_json::Map<String, Value>,
) -> Result<ToolCall, AppError> {
Ok(ToolCall {
id: "tool_call".to_string(),
tool_type: "function".to_string(),
function: FunctionCall {
name: canonical_tool_name,
arguments: serde_json::to_string(&args).map_err(AppError::SerializationError)?,
},
})
}
pub(super) fn requires_session_context(tool_name: &str) -> bool {
matches!(
tool_name,
"Write"
| "Edit"
| "SetWorkspace"
| "Workspace"
| "Task"
| "SubSession"
| "memory"
| "session_note"
| "memory_note"
| "scheduler"
| "schedule_tasks"
| "session_history"
| "recall"
| "session_inspector"
)
}