a3s 0.10.5

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

const MAX_REMOTE_COMMAND_CHARS: usize = 512;
pub(super) const MAX_REMOTE_LIST_PAGE: u16 = 100;

#[derive(Clone, Debug, PartialEq, Eq)]
pub(in crate::api::code_web) enum RemoteIntent {
    Help,
    ListTargets { page: u16 },
    ListSessions { page: u16 },
    Select { reference: String },
    ClearSelection,
    Progress,
    LatestReply,
}

pub(in crate::api::code_web) fn parse_remote_intent(
    text: &str,
) -> Result<RemoteIntent, RemoteIntentError> {
    let text = text.trim();
    if text.is_empty() {
        return Err(RemoteIntentError::Empty);
    }
    if text.chars().count() > MAX_REMOTE_COMMAND_CHARS || text.contains('\0') {
        return Err(RemoteIntentError::InvalidLength);
    }
    let normalized = text.strip_prefix('/').unwrap_or(text).trim();
    match normalized {
        "帮助" => Ok(RemoteIntent::Help),
        "进度" => Ok(RemoteIntent::Progress),
        "最近回复" => Ok(RemoteIntent::LatestReply),
        "清除选择" | "取消选择" => Ok(RemoteIntent::ClearSelection),
        _ => parse_list(normalized, "智能体", |page| RemoteIntent::ListTargets {
            page,
        })
        .or_else(|| {
            parse_list(normalized, "会话", |page| RemoteIntent::ListSessions {
                page,
            })
        })
        .or_else(|| parse_selection(normalized))
        .ok_or(RemoteIntentError::Unsupported),
    }
}

fn parse_list(
    text: &str,
    command: &str,
    intent: impl FnOnce(u16) -> RemoteIntent,
) -> Option<RemoteIntent> {
    let remainder = text.strip_prefix(command)?;
    let page = if remainder.is_empty() {
        1
    } else {
        let remainder = remainder.trim();
        if remainder.is_empty() {
            1
        } else {
            let page = remainder.parse::<u16>().ok()?;
            (1..=MAX_REMOTE_LIST_PAGE).contains(&page).then_some(page)?
        }
    };
    Some(intent(page))
}

fn parse_selection(text: &str) -> Option<RemoteIntent> {
    let reference = text.strip_prefix("选择")?.trim();
    if reference.is_empty()
        || reference.chars().count() > 32
        || reference.chars().any(char::is_control)
    {
        return None;
    }
    Some(RemoteIntent::Select {
        reference: reference.to_string(),
    })
}

#[derive(Clone, Copy, Debug, Error, PartialEq, Eq)]
pub(in crate::api::code_web) enum RemoteIntentError {
    #[error("remote command is empty")]
    Empty,
    #[error("remote command length is invalid")]
    InvalidLength,
    #[error("remote command is unsupported")]
    Unsupported,
    #[error("remote command is ambiguous")]
    Ambiguous,
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn deterministic_remote_parser_accepts_only_closed_read_intents() {
        assert_eq!(parse_remote_intent("帮助"), Ok(RemoteIntent::Help));
        assert_eq!(
            parse_remote_intent("智能体"),
            Ok(RemoteIntent::ListTargets { page: 1 })
        );
        assert_eq!(
            parse_remote_intent("智能体 2"),
            Ok(RemoteIntent::ListTargets { page: 2 })
        );
        assert_eq!(
            parse_remote_intent("会话 100"),
            Ok(RemoteIntent::ListSessions { page: 100 })
        );
        assert_eq!(
            parse_remote_intent("选择 2"),
            Ok(RemoteIntent::Select {
                reference: "2".to_string()
            })
        );
        assert_eq!(
            parse_remote_intent("/最近回复"),
            Ok(RemoteIntent::LatestReply)
        );
        assert_eq!(
            parse_remote_intent("运行 shell rm -rf /"),
            Err(RemoteIntentError::Unsupported)
        );
        assert_eq!(
            parse_remote_intent("批准永久权限"),
            Err(RemoteIntentError::Unsupported)
        );
        assert_eq!(
            parse_remote_intent("智能体 0"),
            Err(RemoteIntentError::Unsupported)
        );
        assert_eq!(
            parse_remote_intent("会话 101"),
            Err(RemoteIntentError::Unsupported)
        );
    }
}