a3s 0.10.7

a3s — A3S coding agent CLI; `a3s code` launches the interactive TUI
Documentation
use super::*;

pub(super) fn parent_presence_state(
    state: State,
    plan_in_progress: bool,
    recent_terminal: Option<AgentActivityState>,
) -> AgentActivityState {
    match state {
        State::Awaiting => AgentActivityState::WaitingApproval,
        State::Rebuilding => AgentActivityState::Working,
        State::Streaming if plan_in_progress => AgentActivityState::Planning,
        State::Streaming => AgentActivityState::Working,
        State::Idle => recent_terminal.unwrap_or(AgentActivityState::Idle),
    }
}

pub(super) fn child_presence(
    id: String,
    child: &runtime_projection::SubagentRun,
    fallback_vendor: AgentVendor,
    now: Instant,
    now_ms: u64,
) -> Option<AgentChildPresence> {
    if child
        .ended
        .is_some_and(|ended| now.saturating_duration_since(ended) > TERMINAL_STATE_RETENTION)
    {
        return None;
    }

    let state = match child.outcome {
        Some(runtime_projection::SubagentOutcome::Succeeded) => AgentActivityState::Completed,
        Some(runtime_projection::SubagentOutcome::Failed) => AgentActivityState::Failed,
        Some(runtime_projection::SubagentOutcome::Cancelled) => AgentActivityState::Cancelled,
        Some(runtime_projection::SubagentOutcome::TrackingLost) => AgentActivityState::Unknown,
        None => AgentActivityState::Working,
    };
    Some(AgentChildPresence {
        id,
        agent: child.display_agent(),
        task: nonempty_presence_text(&child.description),
        state,
        vendor: AgentVendor::from_hint(&child.agent).unwrap_or(fallback_vendor),
        started_at_ms: Some(
            now_ms.saturating_sub(
                now.saturating_duration_since(child.started)
                    .as_millis()
                    .min(u128::from(u64::MAX)) as u64,
            ),
        ),
        finished_at_ms: child.ended.map(|ended| {
            now_ms.saturating_sub(
                now.saturating_duration_since(ended)
                    .as_millis()
                    .min(u128::from(u64::MAX)) as u64,
            )
        }),
        actions: Vec::new(),
    })
}

fn nonempty_presence_text(value: &str) -> Option<String> {
    let value = value.split_whitespace().collect::<Vec<_>>().join(" ");
    (!value.is_empty()).then_some(value)
}