use std::collections::HashSet;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use super::AgentTool;
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ToolProvenance {
#[default]
Native,
McpRemote { server: String },
Plugin {
name: String,
version: String,
carrier: String,
},
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct ToolContinuation {
pub tool: String,
pub backend: String,
pub reason: String,
#[serde(default)]
pub permission_preview: Option<String>,
}
impl ToolContinuation {
#[must_use]
pub fn disclose_backend(
tool: impl Into<String>,
backend: impl Into<String>,
reason: impl Into<String>,
) -> Self {
Self {
tool: tool.into(),
backend: backend.into(),
reason: reason.into(),
permission_preview: None,
}
}
#[must_use]
pub fn disclose_tool(tool: impl Into<String>, reason: impl Into<String>) -> Self {
Self {
tool: tool.into(),
backend: String::new(),
reason: reason.into(),
permission_preview: None,
}
}
#[must_use]
pub fn is_tool_disclosure(&self) -> bool {
self.backend.is_empty()
}
#[must_use]
pub fn with_permission_preview(mut self, preview: impl Into<String>) -> Self {
self.permission_preview = Some(preview.into());
self
}
}
#[derive(Debug, Clone)]
pub struct ToolResult {
pub content: String,
pub is_error: bool,
pub continuations: Vec<ToolContinuation>,
}
#[derive(Debug, Clone)]
pub struct ToolExecutionOutput {
pub result: ToolResult,
pub next_provider_parts: Vec<crate::message::ContentPart>,
}
impl ToolExecutionOutput {
pub fn success(content: impl Into<String>) -> Self {
Self {
result: ToolResult::success(content),
next_provider_parts: Vec::new(),
}
}
pub fn error(content: impl Into<String>) -> Self {
Self {
result: ToolResult::error(content),
next_provider_parts: Vec::new(),
}
}
pub fn from_result(result: ToolResult) -> Self {
Self {
result,
next_provider_parts: Vec::new(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ToolResultProjection {
pub model_content: String,
pub display_content: String,
pub persistence_content: String,
}
impl ToolResultProjection {
#[must_use]
pub fn shared(content: impl Into<String>) -> Self {
let content = content.into();
Self {
model_content: content.clone(),
display_content: content.clone(),
persistence_content: content,
}
}
}
impl ToolResult {
pub fn success(content: impl Into<String>) -> Self {
Self {
content: content.into(),
is_error: false,
continuations: Vec::new(),
}
}
pub fn error(content: impl Into<String>) -> Self {
Self {
content: content.into(),
is_error: true,
continuations: Vec::new(),
}
}
#[must_use]
pub fn with_continuation(mut self, continuation: ToolContinuation) -> Self {
self.continuations.push(continuation);
self
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub enum ToolNature {
#[default]
Read,
Write,
Execute,
Network,
Internal,
}
#[derive(
Debug,
Clone,
Copy,
Default,
PartialEq,
Eq,
Hash,
PartialOrd,
Ord,
Serialize,
Deserialize,
JsonSchema,
)]
#[serde(rename_all = "snake_case")]
pub enum ToolFamily {
#[default]
File,
Search,
CodeIntelligence,
Git,
Network,
AdvancedNetwork,
Shell,
Extension,
Plugin,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct ToolBackend {
pub id: String,
pub description: String,
}
impl ToolBackend {
#[must_use]
pub fn new(id: impl Into<String>, description: impl Into<String>) -> Self {
Self {
id: id.into(),
description: description.into(),
}
}
}
#[derive(
Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, JsonSchema,
)]
pub struct ToolBackendDisclosure {
pub tool: String,
pub backend: String,
}
impl ToolBackendDisclosure {
#[must_use]
pub fn new(tool: impl Into<String>, backend: impl Into<String>) -> Self {
Self {
tool: tool.into(),
backend: backend.into(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct ToolPresentationPolicy {
pub include_all: bool,
pub include_always_on: bool,
#[serde(default)]
pub families: Vec<ToolFamily>,
#[serde(default)]
pub tools: Vec<String>,
#[serde(default)]
pub backends: Vec<ToolBackendDisclosure>,
}
impl ToolPresentationPolicy {
#[must_use]
pub fn full() -> Self {
Self {
include_all: true,
include_always_on: true,
families: Vec::new(),
tools: Vec::new(),
backends: Vec::new(),
}
}
#[must_use]
pub fn always_on() -> Self {
Self {
include_all: false,
include_always_on: true,
families: Vec::new(),
tools: Vec::new(),
backends: Vec::new(),
}
}
#[must_use]
pub fn runtime_default() -> Self {
Self {
include_all: false,
include_always_on: true,
families: vec![
ToolFamily::File,
ToolFamily::Search,
ToolFamily::CodeIntelligence,
ToolFamily::Git,
ToolFamily::Network,
ToolFamily::Shell,
ToolFamily::Extension,
],
tools: Vec::new(),
backends: Vec::new(),
}
}
#[must_use]
pub fn with_families(families: impl IntoIterator<Item = ToolFamily>) -> Self {
Self {
include_all: false,
include_always_on: true,
families: families.into_iter().collect(),
tools: Vec::new(),
backends: Vec::new(),
}
}
#[must_use]
pub fn with_backend(tool: impl Into<String>, backend: impl Into<String>) -> Self {
Self {
include_all: false,
include_always_on: true,
families: Vec::new(),
tools: Vec::new(),
backends: vec![ToolBackendDisclosure::new(tool, backend)],
}
}
#[must_use]
pub fn with_tool(tool: impl Into<String>) -> Self {
Self {
include_all: false,
include_always_on: true,
families: Vec::new(),
tools: vec![tool.into()],
backends: Vec::new(),
}
}
#[must_use]
pub fn disclose_tool(mut self, tool: impl Into<String>) -> Self {
self.tools.push(tool.into());
self
}
#[must_use]
pub fn disclose_backend(mut self, tool: impl Into<String>, backend: impl Into<String>) -> Self {
self.backends
.push(ToolBackendDisclosure::new(tool, backend));
self
}
#[must_use]
pub fn allows_tool(&self, tool: &dyn AgentTool) -> bool {
self.include_all
|| (self.include_always_on && tool.is_always_on())
|| self.families.contains(&tool.family())
|| self.tools.iter().any(|name| name == tool.name())
|| self.backends.iter().any(|entry| entry.tool == tool.name())
}
#[must_use]
pub fn allows_backend(&self, tool: &str, backend: &str) -> bool {
self.include_all
|| self
.backends
.iter()
.any(|entry| entry.tool == tool && entry.backend == backend)
}
#[must_use]
pub fn family_set(&self) -> HashSet<ToolFamily> {
self.families.iter().copied().collect()
}
#[must_use]
pub fn backend_set_for(&self, tool: &str) -> HashSet<String> {
self.backends
.iter()
.filter(|entry| entry.tool == tool)
.map(|entry| entry.backend.clone())
.collect()
}
}
impl Default for ToolPresentationPolicy {
fn default() -> Self {
Self::full()
}
}