Skip to main content

agent_workspace_contract/environment/
skills.rs

1use super::*;
2
3/// Product path for installing a Skills Hub package into a running Sandbox.
4pub const SANDBOX_SKILLS_PATH: &str = "/internal/v1/workspace/sandboxes/{sandboxId}/skills";
5
6/// Product path for installing a Skills Hub package into a Runtime-bound Space.
7pub const SPACE_SKILLS_PATH: &str = "/internal/v1/workspace/spaces/{spaceId}/skills";
8
9/// Product path for searching published Skills Hub packages.
10pub const SKILLS_SEARCH_PATH: &str = "/internal/v1/workspace/skills:search";
11
12/// Sandbox-root directory that receives unpacked skill packages.
13pub const SANDBOX_SKILL_INSTALL_ROOT: &str = "skills";
14
15/// Space-relative directory that Runtime workspace tools expose to the agent.
16pub const SPACE_SKILL_INSTALL_ROOT: &str = "skills";
17
18/// Default Skills Hub version selector when the caller omits `version`.
19pub const SANDBOX_SKILL_DEFAULT_VERSION: &str = "latest";
20
21/// Default sandbox provider when the caller omits `provider`.
22pub const SANDBOX_SKILL_DEFAULT_PROVIDER: &str = "local";
23
24fn default_sandbox_skill_provider() -> String {
25    SANDBOX_SKILL_DEFAULT_PROVIDER.to_string()
26}
27
28#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
29#[serde(deny_unknown_fields, rename_all = "camelCase")]
30pub struct InstallSandboxSkillRequest {
31    pub group: String,
32    pub name: String,
33    #[serde(default, skip_serializing_if = "Option::is_none")]
34    pub version: Option<String>,
35    /// Sandbox provider to write into: `local`, `e2b`, or `moulin`.
36    #[serde(default = "default_sandbox_skill_provider")]
37    pub provider: String,
38}
39
40#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
41#[serde(deny_unknown_fields, rename_all = "camelCase")]
42pub struct InstallSandboxSkillResource {
43    pub sandbox_id: String,
44    pub provider: String,
45    pub group: String,
46    pub name: String,
47    pub version: String,
48    pub install_path: String,
49    pub files: Vec<String>,
50}
51
52#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
53#[serde(deny_unknown_fields, rename_all = "camelCase")]
54pub struct InstallSpaceSkillRequest {
55    pub group: String,
56    pub name: String,
57    #[serde(default, skip_serializing_if = "Option::is_none")]
58    pub version: Option<String>,
59    /// Computer provider that owns the Space: `local`, `e2b`, or `moulin`.
60    #[serde(default = "default_sandbox_skill_provider")]
61    pub provider: String,
62}
63
64#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
65#[serde(deny_unknown_fields, rename_all = "camelCase")]
66pub struct InstallSpaceSkillResource {
67    pub space_id: String,
68    pub computer_id: String,
69    pub provider: String,
70    pub group: String,
71    pub name: String,
72    pub version: String,
73    pub install_path: String,
74    pub files: Vec<String>,
75    pub revision: u64,
76}
77
78#[derive(Debug, Clone, PartialEq, Eq)]
79pub struct SkillPackageFile {
80    pub path: String,
81    pub content: Vec<u8>,
82}
83
84#[derive(Debug, Clone, PartialEq, Eq)]
85pub struct SkillPackage {
86    pub group: String,
87    pub name: String,
88    pub version: String,
89    pub files: Vec<SkillPackageFile>,
90}
91
92#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
93#[serde(rename_all = "camelCase")]
94pub struct SkillSearchQuery {
95    #[serde(default, skip_serializing_if = "Option::is_none")]
96    pub q: Option<String>,
97    #[serde(default, skip_serializing_if = "Option::is_none")]
98    pub page: Option<u32>,
99    #[serde(default, skip_serializing_if = "Option::is_none")]
100    pub limit: Option<u32>,
101}
102
103#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
104#[serde(rename_all = "camelCase")]
105pub struct SkillSearchItem {
106    pub group: String,
107    pub name: String,
108    pub full_name: String,
109    #[serde(default)]
110    pub description: String,
111    #[serde(default)]
112    pub latest_version: String,
113    #[serde(default, skip_serializing_if = "Option::is_none")]
114    pub skill_type: Option<String>,
115}
116
117#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
118#[serde(rename_all = "camelCase")]
119pub struct SkillSearchPage {
120    pub items: Vec<SkillSearchItem>,
121    #[serde(default)]
122    pub page: u32,
123    #[serde(default)]
124    pub limit: u32,
125    #[serde(default, skip_serializing_if = "Option::is_none")]
126    pub total: Option<u64>,
127    #[serde(default, skip_serializing_if = "Option::is_none")]
128    pub has_more: Option<bool>,
129}
130
131/// Downloads a published skill archive. Implementations live in adapter crates.
132#[async_trait]
133pub trait SkillHubClient: Send + Sync + Debug {
134    async fn download_skill(
135        &self,
136        group: &str,
137        name: &str,
138        version: &str,
139        user_authorization: &str,
140    ) -> Result<SkillPackage>;
141
142    async fn search_skills(
143        &self,
144        query: &SkillSearchQuery,
145        user_authorization: &str,
146    ) -> Result<SkillSearchPage>;
147}