use agent_sdk_core::{
AgentError, CapabilityId, CapabilityNamespace, CapabilityPermission, ExecutorRef,
PackageSidecarRef, PolicyKind, PolicyRef, PrivacyClass, SourceRef, ToolPackId, ToolPackKind,
ToolPackSnapshot, ToolPackToolSnapshot, TrustClass,
policy::{EffectClass, RiskClass},
tool_records::CanonicalToolName,
};
use crate::packs::ToolkitPackBundle;
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ToolkitToolExecutionMode {
Sync,
Async,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Tool {
snapshot: ToolPackToolSnapshot,
mode: ToolkitToolExecutionMode,
}
impl Tool {
pub fn builder(
tool_name: impl Into<String>,
executor_ref: impl Into<String>,
schema_id: impl Into<String>,
policy_ref: PolicyRef,
) -> ToolBuilder {
ToolBuilder::new(tool_name, executor_ref, schema_id, policy_ref)
}
pub fn canonical_tool_name(&self) -> &CanonicalToolName {
&self.snapshot.canonical_tool_name
}
pub fn executor_ref(&self) -> &ExecutorRef {
&self.snapshot.executor_ref
}
pub fn mode(&self) -> &ToolkitToolExecutionMode {
&self.mode
}
pub fn snapshot(&self) -> &ToolPackToolSnapshot {
&self.snapshot
}
pub fn into_snapshot(self) -> ToolPackToolSnapshot {
self.snapshot
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct AsyncTool {
snapshot: ToolPackToolSnapshot,
mode: ToolkitToolExecutionMode,
}
impl AsyncTool {
pub fn builder(
tool_name: impl Into<String>,
executor_ref: impl Into<String>,
schema_id: impl Into<String>,
policy_ref: PolicyRef,
) -> ToolBuilder {
ToolBuilder::new(tool_name, executor_ref, schema_id, policy_ref).async_mode()
}
pub fn canonical_tool_name(&self) -> &CanonicalToolName {
&self.snapshot.canonical_tool_name
}
pub fn executor_ref(&self) -> &ExecutorRef {
&self.snapshot.executor_ref
}
pub fn mode(&self) -> &ToolkitToolExecutionMode {
&self.mode
}
pub fn snapshot(&self) -> &ToolPackToolSnapshot {
&self.snapshot
}
pub fn into_snapshot(self) -> ToolPackToolSnapshot {
self.snapshot
}
}
#[derive(Clone, Debug)]
pub struct ToolBuilder {
tool_name: String,
executor_ref: String,
schema_id: String,
policy_refs: Vec<PolicyRef>,
capability_id: Option<CapabilityId>,
namespace: Option<CapabilityNamespace>,
required_permissions: Vec<CapabilityPermission>,
effect_class: Option<EffectClass>,
risk_class: Option<RiskClass>,
redaction_policy_ref: PolicyRef,
timeout_ms: u64,
cancellation: String,
reconciliation: String,
privacy: PrivacyClass,
mode: ToolkitToolExecutionMode,
}
impl ToolBuilder {
pub fn new(
tool_name: impl Into<String>,
executor_ref: impl Into<String>,
schema_id: impl Into<String>,
policy_ref: PolicyRef,
) -> Self {
Self {
tool_name: tool_name.into(),
executor_ref: executor_ref.into(),
schema_id: schema_id.into(),
policy_refs: vec![policy_ref],
capability_id: None,
namespace: None,
required_permissions: Vec::new(),
effect_class: None,
risk_class: None,
redaction_policy_ref: PolicyRef::with_kind(
PolicyKind::Redaction,
"policy.redaction.tool_result.refs_only",
),
timeout_ms: 10_000,
cancellation: "best_effort".to_string(),
reconciliation: "effect_lineage_required".to_string(),
privacy: PrivacyClass::ContentRefsOnly,
mode: ToolkitToolExecutionMode::Sync,
}
}
pub fn read_only(mut self) -> Self {
self.effect_class = Some(EffectClass::Read);
self.risk_class = Some(RiskClass::Low);
self
}
pub fn write_effect(mut self) -> Self {
self.effect_class = Some(EffectClass::Write);
self.risk_class = Some(RiskClass::High);
self
}
pub fn effect(mut self, effect_class: EffectClass, risk_class: RiskClass) -> Self {
self.effect_class = Some(effect_class);
self.risk_class = Some(risk_class);
self
}
pub fn capability_id(mut self, capability_id: CapabilityId) -> Self {
self.capability_id = Some(capability_id);
self
}
pub fn namespace(mut self, namespace: CapabilityNamespace) -> Self {
self.namespace = Some(namespace);
self
}
pub fn required_permission(mut self, permission: CapabilityPermission) -> Self {
self.required_permissions.push(permission);
self
}
pub fn policy_ref(mut self, policy_ref: PolicyRef) -> Self {
self.policy_refs.push(policy_ref);
self
}
pub fn redaction_policy(mut self, policy_ref: PolicyRef) -> Self {
self.redaction_policy_ref = policy_ref;
self
}
pub fn timeout_ms(mut self, timeout_ms: u64) -> Self {
self.timeout_ms = timeout_ms;
self
}
pub fn cancellation(mut self, cancellation: impl Into<String>) -> Self {
self.cancellation = cancellation.into();
self
}
pub fn reconciliation(mut self, reconciliation: impl Into<String>) -> Self {
self.reconciliation = reconciliation.into();
self
}
pub fn privacy(mut self, privacy: PrivacyClass) -> Self {
self.privacy = privacy;
self
}
pub fn async_mode(mut self) -> Self {
self.mode = ToolkitToolExecutionMode::Async;
self.timeout_ms = self.timeout_ms.max(60_000);
self.cancellation = "cooperative".to_string();
self
}
pub fn build(self) -> Result<Tool, AgentError> {
let mode = self.mode.clone();
Ok(Tool {
snapshot: self.build_snapshot()?,
mode,
})
}
pub fn build_async(self) -> Result<AsyncTool, AgentError> {
let builder = self.async_mode();
let mode = builder.mode.clone();
Ok(AsyncTool {
snapshot: builder.build_snapshot()?,
mode,
})
}
fn build_snapshot(self) -> Result<ToolPackToolSnapshot, AgentError> {
let effect_class = self
.effect_class
.ok_or_else(|| AgentError::missing_required_field("tool.effect_class"))?;
let risk_class = self
.risk_class
.ok_or_else(|| AgentError::missing_required_field("tool.risk_class"))?;
let capability_id = self
.capability_id
.unwrap_or_else(|| CapabilityId::new(format!("cap.tool.{}", self.tool_name)));
let namespace = self
.namespace
.unwrap_or_else(|| CapabilityNamespace::new(format!("tool.{}", self.tool_name)));
Ok(ToolPackToolSnapshot {
capability_id,
canonical_tool_name: CanonicalToolName::new(self.tool_name),
namespace,
schema_ref: PackageSidecarRef::new(self.schema_id, "tool_schema", "v1"),
executor_ref: ExecutorRef::new(self.executor_ref),
policy_refs: self.policy_refs,
required_permissions: self.required_permissions,
effect_class,
risk_class,
redaction_policy_ref: self.redaction_policy_ref,
timeout_ms: self.timeout_ms,
cancellation: self.cancellation,
reconciliation: self.reconciliation,
privacy: self.privacy,
})
}
}
#[derive(Clone, Debug)]
pub struct ToolPackBuilder {
pack_id: ToolPackId,
kind: ToolPackKind,
version: String,
source: SourceRef,
trust: TrustClass,
tools: Vec<ToolPackToolSnapshot>,
}
impl ToolPackBuilder {
pub fn new(
pack_id: ToolPackId,
kind: ToolPackKind,
version: impl Into<String>,
source: SourceRef,
) -> Self {
Self {
pack_id,
kind,
version: version.into(),
source,
trust: TrustClass::SdkGenerated,
tools: Vec::new(),
}
}
pub fn trust(mut self, trust: TrustClass) -> Self {
self.trust = trust;
self
}
pub fn listen(mut self, tool: Tool) -> Self {
self.tools.push(tool.into_snapshot());
self
}
pub fn listen_async(mut self, tool: AsyncTool) -> Self {
self.tools.push(tool.into_snapshot());
self
}
pub fn build(self) -> Result<ToolkitPackBundle, AgentError> {
let mut snapshot =
ToolPackSnapshot::new(self.pack_id, self.kind, self.version, self.source)
.with_trust(self.trust);
for tool in self.tools {
snapshot = snapshot.with_tool(tool);
}
ToolkitPackBundle::from_snapshot(snapshot)
}
}