rho-coding-agent 2.3.0

A fast Rust agent harness with a small footprint and opinionated defaults
use serde::{Deserialize, Serialize};
use std::time::Duration;

#[derive(Clone, Debug)]
pub struct ProcessLimits {
    pub max_live: usize,
    pub max_records: usize,
    pub max_bytes: usize,
    pub max_chunks: usize,
    pub retention: Duration,
}
impl Default for ProcessLimits {
    fn default() -> Self {
        Self {
            max_live: 16,
            max_records: 64,
            max_bytes: 1024 * 1024,
            max_chunks: 8192,
            retention: Duration::from_secs(30 * 60),
        }
    }
}
#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum State {
    Starting,
    Running,
    Exited,
    Terminated,
    TimedOut,
    FailedToStart,
}

impl State {
    pub(crate) const fn is_live(self) -> bool {
        matches!(self, Self::Starting | Self::Running)
    }

    pub(crate) const fn as_wire_str(self) -> &'static str {
        match self {
            Self::Starting => "starting",
            Self::Running => "running",
            Self::Exited => "exited",
            Self::Terminated => "terminated",
            Self::TimedOut => "timed_out",
            Self::FailedToStart => "failed_to_start",
        }
    }
}
#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum Stream {
    Stdout,
    Stderr,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
pub struct Chunk {
    pub cursor: u64,
    pub stream: Stream,
    pub text: String,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Snapshot {
    pub process_id: String,
    pub command: String,
    pub state: State,
    pub runtime_seconds: f64,
    pub first_cursor: u64,
    pub next_cursor: u64,
    pub available_cursor: u64,
    pub truncated: bool,
    pub output_pending: bool,
    pub chunks: Vec<Chunk>,
    pub exit_code: Option<i32>,
    pub terminal_detail: Option<String>,
}
pub(super) fn terminal(s: State) -> bool {
    matches!(
        s,
        State::Exited | State::Terminated | State::TimedOut | State::FailedToStart
    )
}

/// Host-UI-only retained output for one process. Not a tool action.
///
/// Does not mark the process observed or advance any agent-visible poll cursor.
#[derive(Clone, Debug)]
pub(crate) struct HostProcessView {
    pub(crate) snapshot: Snapshot,
    pub(crate) elapsed_seconds: u64,
    /// Seconds since the most recent output chunk. None when the process has
    /// produced no output yet, and None for terminal-state rows.
    pub(crate) quiet_seconds: Option<u64>,
}

/// Host-side view of one live process for UI rails. Not a tool action.
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct LiveProcessSummary {
    pub(crate) process_id: String,
    pub(crate) command: String,
    pub(crate) state: State,
    pub(crate) elapsed_seconds: u64,
    /// Seconds since the most recent output chunk. None when the process has
    /// produced no output yet, and None for terminal-state rows (the UI shows
    /// a verdict instead).
    pub(crate) quiet_seconds: Option<u64>,
    /// Populated for terminal rows from the process exit code. None for live rows.
    pub(crate) exit_code: Option<i32>,
}