agent-workspace-contract 0.5.2

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

/// Provider-owned images used when product create APIs omit every Workspace
/// catalog snapshot/template selector. A configured catalog default still
/// takes precedence and is resolved to its provider external id by Runtime.
pub const E2B_PLATFORM_TEMPLATE_ID: &str = "code-interpreter-v1";
pub const E2B_BUILTIN_TEMPLATE_IDS: [&str; 3] = [
    E2B_PLATFORM_TEMPLATE_ID,
    "context-management-moc",
    "vpc-sandbox",
];
pub const MOULIN_PLATFORM_SNAPSHOT_ID: &str = "python";

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum SnapshotVisibility {
    #[default]
    Private,
    Public,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum SnapshotBuildSpec {
    Dockerfile {
        dockerfile: String,
    },
    Template {
        #[serde(alias = "pipPackages")]
        pip_packages: String,
    },
    Git {
        #[serde(rename = "gitUrl", alias = "git_url")]
        git_url: String,
        #[serde(default, rename = "gitRef", alias = "git_ref")]
        git_ref: String,
    },
}

/// Product-facing snapshot recipe size limits. The build adapters impose
/// additional limits on fetched repository contents and generated archives.
pub const SNAPSHOT_DOCKERFILE_MAX_BYTES: usize = 1024 * 1024;
pub const SNAPSHOT_TEMPLATE_PACKAGES_MAX_BYTES: usize = 64 * 1024;

#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct EnvironmentCatalogConfig {
    pub default_provider: Option<String>,
    pub default_snapshot_id: Option<String>,
    pub default_builds: BTreeMap<String, SnapshotBuildSpec>,
}

#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields, rename_all = "camelCase")]
pub struct CreateSnapshotResourceRequest {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub provider: Option<String>,
    #[serde(default)]
    pub visibility: SnapshotVisibility,
    #[serde(default)]
    pub owners: Vec<OwnerRef>,
    #[serde(default)]
    pub builtin: bool,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub build: Option<SnapshotBuildSpec>,
}

#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields, rename_all = "camelCase")]
pub struct BuildBuiltinSnapshotRequest {
    pub provider: String,
    #[serde(default)]
    pub set_default: bool,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub git_url: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub git_ref: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub dockerfile: Option<String>,
}

pub const ADMIN_BUILD_BUILTIN_SNAPSHOTS_PATH: &str = "/admin/api/workspace-snapshots:build-builtin";
pub const ADMIN_SNAPSHOTS_PATH: &str = "/admin/api/workspace-snapshots";
pub const ADMIN_SNAPSHOT_PATH: &str = "/admin/api/workspace-snapshots/{snapshotId}";
pub const CATALOG_SNAPSHOT_PATH: &str = "/internal/v1/workspace/snapshots:catalog/{snapshotId}";

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields, rename_all = "camelCase")]
pub struct ProviderBuildHandle {
    pub build_id: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub resource_external_id: Option<String>,
}

/// Provider build observation captured by caller-driven polling. Logs are
/// bounded by the adapter/runtime and never contain provider credentials.
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct EnvironmentBuildObservation {
    pub stage: Option<String>,
    pub logs: Vec<String>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EnvironmentBuildStatus {
    Building(ProviderBuildHandle),
    BuildingObserved {
        handle: ProviderBuildHandle,
        observation: EnvironmentBuildObservation,
    },
    Ready(ProviderResourceRef),
    ReadyWithMetadata {
        provider_ref: ProviderResourceRef,
        digest: Option<String>,
        size_bytes: Option<u64>,
    },
    ReadyObserved {
        provider_ref: ProviderResourceRef,
        digest: Option<String>,
        size_bytes: Option<u64>,
        observation: EnvironmentBuildObservation,
    },
    Failed(String),
    FailedObserved {
        error: String,
        observation: EnvironmentBuildObservation,
    },
}

/// Build-only port. Implementations must not create Computer or Sandbox instances.
#[async_trait]
pub trait EnvironmentBuildPort: Send + Sync + Debug {
    async fn submit_environment_image(
        &self,
        provider: &str,
        spec: &SnapshotBuildSpec,
        idempotency_key: &str,
    ) -> Result<EnvironmentBuildStatus>;

    async fn poll_environment_image(
        &self,
        _provider: &str,
        _handle: &ProviderBuildHandle,
    ) -> Result<EnvironmentBuildStatus> {
        Err(anyhow::anyhow!(
            "build provider does not support caller-driven status polling"
        ))
    }
}