mars-agents 0.8.0

Agent package manager for .agents/ directories
Documentation
use std::collections::BTreeMap;

use serde::{Deserialize, Serialize};

pub const SLOT_PLACEHOLDER: &str = "###SLOT###";

#[derive(Debug, Clone, Serialize)]
pub struct LaunchBundle {
    pub version: u32,
    pub agent: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub agent_body: Option<String>,
    pub routing: Routing,
    pub execution_policy: ExecutionPolicy,
    /// EXPERIMENTAL — see `build/project` module doc.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub launch_actions: Option<LaunchActions>,
    pub prompt_surface: PromptSurface,
    pub scaffold_slots: ScaffoldSlots,
    pub tools: ToolsSpec,
    pub skills: Skills,
    pub provenance: BTreeMap<String, String>,
    pub warnings: Vec<String>,
}

/// EXPERIMENTAL — see `build/project` module doc.
#[derive(Debug, Clone, Deserialize)]
pub struct RuntimeContext {
    pub cwd: Option<String>,
    pub temp_dir: Option<String>,
    pub streaming: Option<StreamingContext>,
    pub session_id: Option<String>,
    #[serde(default)]
    pub fork: bool,
    #[serde(default)]
    pub workspace_roots: Vec<String>,
    #[serde(default)]
    pub interactive: bool,
    #[serde(default)]
    pub extra_args: Vec<String>,
    pub opencode_config_content: Option<String>,
    #[serde(default)]
    pub pi_extension_entrypoints: Vec<String>,
    pub prompt: Option<String>,
    pub report_output_path: Option<String>,
    pub pi_session_dir: Option<String>,
    #[serde(default)]
    pub load_all_pi_extensions: bool,
}

#[derive(Debug, Clone, Deserialize)]
pub struct StreamingContext {
    pub host: String,
    pub port: u16,
}

#[derive(Debug, Clone, Serialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum LaunchActions {
    Subprocess {
        argv: Vec<String>,
        env: BTreeMap<String, String>,
        cwd: String,
        files: Vec<LaunchFile>,
        stdin: Option<String>,
    },
    Streaming {
        argv: Vec<String>,
        env: BTreeMap<String, String>,
        cwd: String,
        protocol: LaunchProtocol,
    },
}

#[derive(Debug, Clone, Serialize)]
pub struct LaunchFile {
    pub path: String,
    pub content: String,
}

#[derive(Debug, Clone, Serialize)]
pub struct LaunchProtocol {
    pub transport: String,
    pub bootstrap: ProtocolBootstrap,
    pub turn: ProtocolTurn,
}

#[derive(Debug, Clone, Serialize)]
pub struct ProtocolBootstrap {
    pub method: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub path: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub params: Option<serde_json::Value>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub body: Option<serde_json::Value>,
}

#[derive(Debug, Clone, Serialize)]
pub struct ProtocolTurn {
    pub method: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub path_template: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub params_template: Option<serde_json::Value>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub body_template: Option<serde_json::Value>,
}

#[derive(Debug, Clone, Serialize)]
pub struct Routing {
    pub model: String,
    pub model_token: String,
    pub harness: String,
    pub selection_kind: String,
    pub match_evidence: String,
    pub harness_model: String,
    pub harness_model_source: String,
    pub harness_model_confidence: String,
    /// Diagnostic only: probe/catalog slug candidates for the selected harness.
    /// Consumers should run `harness_model` verbatim and ignore this unless debugging.
    pub candidate_slugs: Vec<String>,
    pub route_trace: crate::routing::report::RouteDecisionReport,
}

#[derive(Debug, Clone, Serialize)]
pub struct CodexRule {
    pub name: String,
    pub content: String,
}

#[derive(Debug, Clone, Serialize)]
pub struct ExecutionPolicy {
    pub effort: Option<String>,
    pub approval: Option<String>,
    pub sandbox: Option<String>,
    pub autocompact: Option<u32>,
    pub autocompact_pct: Option<u8>,
    pub timeout: Option<u32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub native_config: Option<serde_json::Map<String, serde_json::Value>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub codex_rules: Option<Vec<CodexRule>>,
}

#[derive(Debug, Clone, Serialize)]
pub struct PromptSurface {
    pub system_instruction: String,
    pub supplemental_documents: Vec<SupplementalDoc>,
    pub inventory_prompt: String,
}

#[derive(Debug, Clone, Serialize)]
pub struct ScaffoldSlots {
    pub completion_contract: String,
    pub context_prompt: String,
    pub user_prompt_file: String,
    pub context_files: String,
    pub prior_session_context: String,
    pub spawn_metadata: String,
}

impl ScaffoldSlots {
    pub fn placeholders() -> Self {
        Self {
            completion_contract: SLOT_PLACEHOLDER.to_string(),
            context_prompt: SLOT_PLACEHOLDER.to_string(),
            user_prompt_file: SLOT_PLACEHOLDER.to_string(),
            context_files: SLOT_PLACEHOLDER.to_string(),
            prior_session_context: SLOT_PLACEHOLDER.to_string(),
            spawn_metadata: SLOT_PLACEHOLDER.to_string(),
        }
    }
}

#[derive(Debug, Clone, Serialize)]
pub struct SupplementalDoc {
    pub kind: String,
    pub name: String,
    pub content: String,
    pub skill_type: String,
}

#[derive(Debug, Clone, Serialize)]
pub struct ToolsSpec {
    pub allowed: Vec<String>,
    pub disallowed: Vec<String>,
    pub mcp: Vec<String>,
}

#[derive(Debug, Clone, Serialize)]
pub struct Skills {
    pub loaded: Vec<LoadedSkill>,
    pub available: Vec<AvailableSkill>,
    pub missing: Vec<String>,
}

#[derive(Debug, Clone, Serialize)]
pub struct LoadedSkill {
    pub name: String,
    pub skill_type: String,
    /// Raw skill body without heading. Consumers render headings themselves.
    pub body: String,
}

#[derive(Debug, Clone, Serialize)]
pub struct AvailableSkill {
    pub name: String,
    pub skill_type: String,
    pub description: String,
}