use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ToolCallStatus {
Pending,
InProgress,
Completed,
Failed,
}
impl ToolCallStatus {
pub const ALL: [Self; 4] = [
Self::Pending,
Self::InProgress,
Self::Completed,
Self::Failed,
];
pub fn as_str(self) -> &'static str {
match self {
Self::Pending => "pending",
Self::InProgress => "in_progress",
Self::Completed => "completed",
Self::Failed => "failed",
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ToolCallErrorCategory {
SchemaValidation,
ToolError,
McpServerError,
HostBridgeError,
PermissionDenied,
RejectedLoop,
ParseAborted,
Timeout,
Network,
Cancelled,
Unknown,
}
impl ToolCallErrorCategory {
pub const ALL: [Self; 11] = [
Self::SchemaValidation,
Self::ToolError,
Self::McpServerError,
Self::HostBridgeError,
Self::PermissionDenied,
Self::RejectedLoop,
Self::ParseAborted,
Self::Timeout,
Self::Network,
Self::Cancelled,
Self::Unknown,
];
pub fn as_str(self) -> &'static str {
match self {
Self::SchemaValidation => "schema_validation",
Self::ToolError => "tool_error",
Self::McpServerError => "mcp_server_error",
Self::HostBridgeError => "host_bridge_error",
Self::PermissionDenied => "permission_denied",
Self::RejectedLoop => "rejected_loop",
Self::ParseAborted => "parse_aborted",
Self::Timeout => "timeout",
Self::Network => "network",
Self::Cancelled => "cancelled",
Self::Unknown => "unknown",
}
}
pub fn from_internal(category: &crate::value::ErrorCategory) -> Self {
use crate::value::ErrorCategory as Internal;
match category {
Internal::Timeout => Self::Timeout,
Internal::RateLimit
| Internal::Overloaded
| Internal::ServerError
| Internal::TransientNetwork => Self::Network,
Internal::SchemaValidation | Internal::SchemaStreamAborted => Self::SchemaValidation,
Internal::ToolError => Self::ToolError,
Internal::ToolRejected => Self::PermissionDenied,
Internal::Cancelled => Self::Cancelled,
Internal::Auth
| Internal::EgressBlocked
| Internal::NotFound
| Internal::CircuitOpen
| Internal::BudgetExceeded
| Internal::Generic => Self::HostBridgeError,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Default, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum DenialGate {
ToolCeiling,
CapabilityCeiling,
SideEffectCeiling,
ArgConstraint,
DynamicPermission,
ApprovalPolicy,
ApprovalUnavailable,
HostRejected,
HookDeny,
#[default]
Unknown,
}
impl DenialGate {
pub const ALL: [Self; 10] = [
Self::ToolCeiling,
Self::CapabilityCeiling,
Self::SideEffectCeiling,
Self::ArgConstraint,
Self::DynamicPermission,
Self::ApprovalPolicy,
Self::ApprovalUnavailable,
Self::HostRejected,
Self::HookDeny,
Self::Unknown,
];
pub fn as_str(self) -> &'static str {
match self {
Self::ToolCeiling => "tool_ceiling",
Self::CapabilityCeiling => "capability_ceiling",
Self::SideEffectCeiling => "side_effect_ceiling",
Self::ArgConstraint => "arg_constraint",
Self::DynamicPermission => "dynamic_permission",
Self::ApprovalPolicy => "approval_policy",
Self::ApprovalUnavailable => "approval_unavailable",
Self::HostRejected => "host_rejected",
Self::HookDeny => "hook_deny",
Self::Unknown => "unknown",
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct ToolDenial {
pub gate: DenialGate,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub capability: Option<String>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub denied_paths: Vec<String>,
pub retryable: bool,
pub reason: String,
}
impl ToolDenial {
pub fn terminal(
gate: DenialGate,
capability: Option<String>,
reason: impl Into<String>,
) -> Self {
Self {
gate,
capability,
denied_paths: Vec::new(),
retryable: false,
reason: reason.into(),
}
}
pub fn to_json(&self) -> serde_json::Value {
serde_json::to_value(self).unwrap_or(serde_json::Value::Null)
}
}
#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum ToolExecutor {
HarnBuiltin,
HostBridge,
McpServer { server_name: String },
ProviderNative,
}