agent-workspace-contract 0.5.1

Transport-neutral contracts for Agent Infra workspace APIs
Documentation
use super::*;

/// Product path for installing a Skills Hub package into a running Sandbox.
pub const SANDBOX_SKILLS_PATH: &str = "/internal/v1/workspace/sandboxes/{sandboxId}/skills";

/// Product path for searching published Skills Hub packages.
pub const SKILLS_SEARCH_PATH: &str = "/internal/v1/workspace/skills:search";

/// Sandbox-root directory that receives unpacked skill packages.
pub const SANDBOX_SKILL_INSTALL_ROOT: &str = "skills";

/// Default Skills Hub version selector when the caller omits `version`.
pub const SANDBOX_SKILL_DEFAULT_VERSION: &str = "latest";

/// Default sandbox provider when the caller omits `provider`.
pub const SANDBOX_SKILL_DEFAULT_PROVIDER: &str = "local";

fn default_sandbox_skill_provider() -> String {
    SANDBOX_SKILL_DEFAULT_PROVIDER.to_string()
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields, rename_all = "camelCase")]
pub struct InstallSandboxSkillRequest {
    pub group: String,
    pub name: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub version: Option<String>,
    /// Sandbox provider to write into: `local`, `e2b`, or `moulin`.
    #[serde(default = "default_sandbox_skill_provider")]
    pub provider: String,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields, rename_all = "camelCase")]
pub struct InstallSandboxSkillResource {
    pub sandbox_id: String,
    pub provider: String,
    pub group: String,
    pub name: String,
    pub version: String,
    pub install_path: String,
    pub files: Vec<String>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SkillPackageFile {
    pub path: String,
    pub content: Vec<u8>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SkillPackage {
    pub group: String,
    pub name: String,
    pub version: String,
    pub files: Vec<SkillPackageFile>,
}

#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SkillSearchQuery {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub q: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub page: Option<u32>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub limit: Option<u32>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SkillSearchItem {
    pub group: String,
    pub name: String,
    pub full_name: String,
    #[serde(default)]
    pub description: String,
    #[serde(default)]
    pub latest_version: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub skill_type: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SkillSearchPage {
    pub items: Vec<SkillSearchItem>,
    #[serde(default)]
    pub page: u32,
    #[serde(default)]
    pub limit: u32,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub total: Option<u64>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub has_more: Option<bool>,
}

/// Downloads a published skill archive. Implementations live in adapter crates.
#[async_trait]
pub trait SkillHubClient: Send + Sync + Debug {
    async fn download_skill(
        &self,
        group: &str,
        name: &str,
        version: &str,
        user_authorization: &str,
    ) -> Result<SkillPackage>;

    async fn search_skills(
        &self,
        query: &SkillSearchQuery,
        user_authorization: &str,
    ) -> Result<SkillSearchPage>;
}