distri_types/configuration/
package.rs

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