distri_types/configuration/
package.rs

1use crate::ToolDefinition;
2use crate::agent::StandardDefinition;
3use crate::configuration::manifest::DistriServerConfig;
4use crate::configuration::workflow::{
5    CustomAgentDefinition, DagWorkflowDefinition, SequentialWorkflowDefinition,
6};
7use serde::{Deserialize, Serialize};
8use std::path::PathBuf;
9
10/// Tool definition ready for DAP registration with runtime info
11#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct PluginToolDefinition {
13    pub name: String,
14    pub package_name: String,
15    pub description: String,
16    #[serde(default)]
17    pub parameters: serde_json::Value,
18    #[serde(default, skip_serializing_if = "Option::is_none")]
19    pub auth: Option<crate::auth::AuthRequirement>,
20}
21
22/// Workflow definition ready for DAP registration with runtime info
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct PluginWorkflowDefinition {
25    pub name: String,
26    pub package_name: String,
27    pub description: String,
28    #[serde(default)]
29    pub parameters: serde_json::Value,
30    #[serde(default, skip_serializing_if = "Vec::is_empty")]
31    pub examples: Vec<serde_json::Value>,
32}
33
34/// Cloud-specific metadata for agents (optional, only present in cloud responses)
35#[derive(Debug, Clone, Serialize, Deserialize, Default)]
36pub struct AgentCloudMetadata {
37    #[serde(default, skip_serializing_if = "Option::is_none")]
38    pub id: Option<uuid::Uuid>,
39    #[serde(default, skip_serializing_if = "Option::is_none")]
40    pub published: Option<bool>,
41    #[serde(default, skip_serializing_if = "Option::is_none")]
42    pub published_at: Option<chrono::DateTime<chrono::Utc>>,
43    #[serde(default, skip_serializing_if = "Option::is_none")]
44    pub is_owner: Option<bool>,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct AgentConfigWithTools {
49    #[serde(flatten)]
50    pub agent: AgentConfig,
51    #[serde(default, skip_serializing_if = "Vec::is_empty")]
52    pub resolved_tools: Vec<ToolDefinition>,
53    #[serde(default, skip_serializing_if = "Option::is_none")]
54    pub markdown: Option<String>,
55    /// Cloud-specific metadata (optional, only present in cloud responses)
56    #[serde(flatten, default)]
57    pub cloud: AgentCloudMetadata,
58}
59
60/// Unified agent configuration enum - combines all agent and workflow types
61#[derive(Debug, Clone, Serialize, Deserialize)]
62#[serde(tag = "agent_type", rename_all = "snake_case")]
63pub enum AgentConfig {
64    /// Standard markdown-based agent
65    StandardAgent(StandardDefinition),
66    /// Sequential workflow agent with ordered steps
67    SequentialWorkflowAgent(SequentialWorkflowDefinition),
68    /// DAG workflow agent with dependency graph
69    DagWorkflowAgent(DagWorkflowDefinition),
70    /// Custom TypeScript-based agent
71    CustomAgent(CustomAgentDefinition),
72}
73
74impl AgentConfig {
75    /// Get the name of the agent/workflow
76    pub fn get_name(&self) -> &str {
77        match self {
78            AgentConfig::StandardAgent(def) => &def.name,
79            AgentConfig::SequentialWorkflowAgent(def) => &def.name,
80            AgentConfig::DagWorkflowAgent(def) => &def.name,
81            AgentConfig::CustomAgent(def) => &def.name,
82        }
83    }
84
85    /// Get the description of the agent/workflow
86    pub fn get_description(&self) -> &str {
87        match self {
88            AgentConfig::StandardAgent(def) => &def.description,
89            AgentConfig::SequentialWorkflowAgent(def) => &def.description,
90            AgentConfig::DagWorkflowAgent(def) => &def.description,
91            AgentConfig::CustomAgent(def) => &def.description,
92        }
93    }
94
95    /// Validate the configuration
96    pub fn validate(&self) -> anyhow::Result<()> {
97        match self {
98            AgentConfig::StandardAgent(def) => def.validate(),
99            AgentConfig::SequentialWorkflowAgent(def) => {
100                if def.name.is_empty() {
101                    return Err(anyhow::anyhow!("Workflow name cannot be empty"));
102                }
103                if def.steps.is_empty() {
104                    return Err(anyhow::anyhow!("Workflow must have at least one step"));
105                }
106                Ok(())
107            }
108            AgentConfig::DagWorkflowAgent(def) => {
109                if def.name.is_empty() {
110                    return Err(anyhow::anyhow!("Workflow name cannot be empty"));
111                }
112                if def.nodes.is_empty() {
113                    return Err(anyhow::anyhow!("DAG workflow must have at least one node"));
114                }
115                Ok(())
116            }
117            AgentConfig::CustomAgent(def) => {
118                if def.name.is_empty() {
119                    return Err(anyhow::anyhow!("Custom agent name cannot be empty"));
120                }
121                if def.script_path.is_empty() {
122                    return Err(anyhow::anyhow!("Custom agent script_path cannot be empty"));
123                }
124                Ok(())
125            }
126        }
127    }
128
129    /// Get the working directory with fallback chain: agent config -> package config -> DISTRI_HOME -> current_dir
130    pub fn get_working_directory(
131        &self,
132        package_config: Option<&DistriServerConfig>,
133    ) -> anyhow::Result<std::path::PathBuf> {
134        // Fall back to package configuration
135        if let Some(config) = package_config {
136            return config.get_working_directory();
137        }
138
139        // Try DISTRI_HOME environment variable
140        if let Ok(distri_home) = std::env::var("DISTRI_HOME") {
141            return Ok(std::path::PathBuf::from(distri_home));
142        }
143
144        // Fallback to current directory
145        std::env::current_dir()
146            .map_err(|e| anyhow::anyhow!("Failed to get current directory: {}", e))
147    }
148}
149
150/// Agent definition ready for DAP registration with runtime info
151#[derive(Debug, Clone, Serialize, Deserialize)]
152pub struct PluginAgentDefinition {
153    pub name: String,
154    pub package_name: String,
155    pub description: String,
156    pub file_path: PathBuf,
157    /// The full agent configuration (supports all agent types)
158    pub agent_config: AgentConfig,
159}
160
161/// Built DAP package artifact ready for registration in distri
162#[derive(Debug, Clone, Serialize, Deserialize)]
163pub struct PluginArtifact {
164    pub name: String,
165    pub path: PathBuf,
166    pub configuration: crate::configuration::manifest::DistriServerConfig,
167    pub tools: Vec<PluginToolDefinition>,
168    pub workflows: Vec<PluginWorkflowDefinition>,
169    pub agents: Vec<PluginAgentDefinition>,
170}