codex-mobile-contracts 0.2.0

Shared domain contracts for codex-mobile client and bridge.
Documentation
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
#[serde(default)]
pub struct StatusSurfaceState {
    pub kind: String,
    pub header: String,
    pub details: Option<String>,
    pub inline_message: Option<String>,
    pub interrupt_visible: bool,
    pub revision: i64,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
#[serde(default)]
pub struct RenderPlanStep {
    pub step: String,
    pub status: String,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
#[serde(default)]
pub struct ExecCommandLine {
    pub label: String,
    pub text: String,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
#[serde(default)]
pub struct FileChangeLine {
    pub path: String,
    pub summary: String,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
#[serde(default)]
pub struct ThreadRenderSnapshot {
    pub runtime_id: String,
    pub thread_id: String,
    pub revision: i64,
    pub status_surface: Option<StatusSurfaceState>,
    #[serde(default)]
    pub nodes: Vec<ThreadRenderNode>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
#[serde(default)]
pub struct ThreadRenderPatch {
    pub runtime_id: String,
    pub thread_id: String,
    pub snapshot: ThreadRenderSnapshot,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ThreadRenderNode {
    UserMessage {
        id: String,
        turn_id: Option<String>,
        item_id: Option<String>,
        text: String,
    },
    AssistantMarkdown {
        id: String,
        turn_id: Option<String>,
        item_id: Option<String>,
        text: String,
        phase: Option<String>,
        streaming: bool,
    },
    FinalSeparator {
        id: String,
        turn_id: Option<String>,
    },
    ReasoningSummary {
        id: String,
        turn_id: Option<String>,
        item_id: Option<String>,
        title: String,
        text: String,
    },
    PlanUpdate {
        id: String,
        turn_id: Option<String>,
        item_id: Option<String>,
        title: String,
        explanation: Option<String>,
        #[serde(default)]
        steps: Vec<RenderPlanStep>,
        streaming: bool,
    },
    ProposedPlan {
        id: String,
        turn_id: Option<String>,
        item_id: Option<String>,
        title: String,
        text: String,
    },
    ExecGroup {
        id: String,
        turn_id: Option<String>,
        item_id: Option<String>,
        title: String,
        state: String,
        #[serde(default)]
        commands: Vec<ExecCommandLine>,
        output_text: Option<String>,
        exit_code: Option<i64>,
    },
    FileChange {
        id: String,
        turn_id: Option<String>,
        item_id: Option<String>,
        title: String,
        state: String,
        #[serde(default)]
        changes: Vec<FileChangeLine>,
    },
    McpToolCall {
        id: String,
        turn_id: Option<String>,
        item_id: Option<String>,
        title: String,
        state: String,
        subtitle: String,
        detail: Option<String>,
    },
    DynamicToolCall {
        id: String,
        turn_id: Option<String>,
        item_id: Option<String>,
        title: String,
        state: String,
        subtitle: String,
        detail: Option<String>,
    },
    WebSearch {
        id: String,
        turn_id: Option<String>,
        item_id: Option<String>,
        title: String,
        state: String,
        detail: Option<String>,
    },
    CollabEvent {
        id: String,
        turn_id: Option<String>,
        item_id: Option<String>,
        title: String,
        #[serde(default)]
        detail_lines: Vec<String>,
    },
    InfoNotice {
        id: String,
        turn_id: Option<String>,
        item_id: Option<String>,
        title: String,
        detail: Option<String>,
    },
    ViewImage {
        id: String,
        turn_id: Option<String>,
        item_id: Option<String>,
        title: String,
        path: String,
    },
    ImageGeneration {
        id: String,
        turn_id: Option<String>,
        item_id: Option<String>,
        title: String,
        state: String,
        prompt: Option<String>,
        result: String,
        saved_path: Option<String>,
    },
}

impl Default for ThreadRenderNode {
    fn default() -> Self {
        Self::InfoNotice {
            id: String::new(),
            turn_id: None,
            item_id: None,
            title: String::new(),
            detail: None,
        }
    }
}