agent-workspace-contract 0.5.1

Transport-neutral contracts for Agent Infra workspace APIs
Documentation
use serde::{Deserialize, Serialize};

use crate::CommandSpec;

pub const COMPUTER_COMMANDS_PATH: &str = "/internal/v1/workspace/computers/{computerId}/commands";
pub const COMPUTER_COMMAND_PATH: &str =
    "/internal/v1/workspace/computers/{computerId}/commands/{commandId}";
pub const COMPUTER_COMMAND_OUTPUT_PATH: &str =
    "/internal/v1/workspace/computers/{computerId}/commands/{commandId}/output";
pub const COMPUTER_COMMAND_CANCEL_PATH: &str =
    "/internal/v1/workspace/computers/{computerId}/commands/{commandId}/cancel";
pub const COMPUTER_OWNERS_PATH: &str = "/internal/v1/workspace/computers/{computerId}/owners";
pub const SPACE_OWNERS_PATH: &str = "/internal/v1/workspace/spaces/{spaceId}/owners";

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ComputerCommandState {
    Queued,
    Running,
    Succeeded,
    Failed,
    Canceling,
    Canceled,
}

pub fn valid_computer_command_transition(
    from: ComputerCommandState,
    to: ComputerCommandState,
) -> bool {
    matches!(
        (from, to),
        (ComputerCommandState::Queued, ComputerCommandState::Running)
            | (ComputerCommandState::Queued, ComputerCommandState::Canceled)
            | (
                ComputerCommandState::Running,
                ComputerCommandState::Succeeded
            )
            | (ComputerCommandState::Running, ComputerCommandState::Failed)
            | (
                ComputerCommandState::Running,
                ComputerCommandState::Canceling
            )
            | (
                ComputerCommandState::Running,
                ComputerCommandState::Canceled
            )
            | (
                ComputerCommandState::Canceling,
                ComputerCommandState::Canceled
            )
            | (
                ComputerCommandState::Canceling,
                ComputerCommandState::Failed
            )
    )
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields, rename_all = "camelCase")]
pub struct CreateComputerCommandRequest {
    pub space_id: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub id: Option<String>,
    pub command: CommandSpec,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields, rename_all = "camelCase")]
pub struct ComputerCommandResource {
    pub id: String,
    pub computer_id: String,
    pub space_id: String,
    pub spec: CommandSpec,
    pub state: ComputerCommandState,
    pub output_end_cursor: u64,
    pub output_truncated: bool,
    pub version: u64,
    pub created_at_ms: u64,
    pub updated_at_ms: u64,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub exit_code: Option<i32>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub error_code: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ComputerCommandOutputPage {
    pub chunks: Vec<ComputerCommandOutputChunk>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub next_cursor: Option<u64>,
    pub truncated: bool,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields, rename_all = "camelCase")]
pub struct ComputerCommandOutputQuery {
    #[serde(default)]
    pub after: u64,
    #[serde(default = "default_command_output_page_limit")]
    pub limit: usize,
}

fn default_command_output_page_limit() -> usize {
    64
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ComputerCommandOutputChunk {
    pub stream: ComputerCommandOutputStream,
    pub content: String,
    pub cursor: u64,
}

/// Durable, scope-qualified command creation input owned by the Workspace
/// command repository. Only hashes of caller idempotency material cross the
/// persistence boundary.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StoreComputerCommandRequest {
    pub scope: crate::ResourceScope,
    pub idempotency_key_hash: String,
    pub request_hash: String,
    pub command: ComputerCommandResource,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum StoreComputerCommandOutcome {
    Created(ComputerCommandResource),
    Replayed(ComputerCommandResource),
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ComputerCommandTransition {
    pub scope: crate::ResourceScope,
    pub id: String,
    pub expected_version: u64,
    pub state: ComputerCommandState,
    pub exit_code: Option<i32>,
    pub error_code: Option<String>,
    pub updated_at_ms: u64,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CompleteComputerCommandRequest {
    pub transition: ComputerCommandTransition,
    pub chunks: Vec<ComputerCommandOutputChunk>,
    pub output_truncated: bool,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ComputerCommandOutputStream {
    Stdout,
    Stderr,
}

/// Synchronous provider command result. The control plane may persist chunks.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ProviderCommandOutput {
    pub exit_code: Option<i32>,
    pub stdout: String,
    pub stderr: String,
    pub truncated: bool,
}