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,
#[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>,
}
#[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,
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,
pub body: String,
}
#[derive(Debug, Clone, Serialize)]
pub struct AvailableSkill {
pub name: String,
pub skill_type: String,
pub description: String,
}