liminal-cli 0.3.32

The liminal command: serve the network, maintain its stores, or serve its discovery MCP over stdio
Documentation
use super::handshake::{Version, negotiated};
use super::queue::Prompts;
use super::stream::project;
use serde_json::json;

#[test]
fn accepts_one_and_two_and_refuses_three_by_name() -> anyhow::Result<()> {
    assert_eq!(negotiated(1)?, Version::One);
    assert_eq!(negotiated(2)?, Version::Two);
    let error = negotiated(3)
        .err()
        .ok_or_else(|| anyhow::anyhow!("3 was admitted"))?;
    assert_eq!(
        error.to_string(),
        "ACP protocol version 3 refused: offered 2, supported 2 and 1"
    );
    Ok(())
}

#[test]
fn thirty_third_unread_prompt_is_refused_without_displacing_mail() -> anyhow::Result<()> {
    let mut queue = Prompts::new(32);
    for n in 0..32 {
        queue.push(n.to_string())?;
    }
    let error = queue
        .push("overflow".to_owned())
        .err()
        .ok_or_else(|| anyhow::anyhow!("overflow admitted"))?;
    assert_eq!(
        error.to_string(),
        "prompt_queue 32 reached: 32 unread prompts"
    );
    for n in 0..32 {
        assert_eq!(queue.pop().as_deref(), Some(n.to_string().as_str()));
    }
    assert!(queue.pop().is_none());
    Ok(())
}

#[test]
fn stream_whitelists_title_and_status_and_never_copies_tool_bytes() -> anyhow::Result<()> {
    let item = project(Version::One, json!({
        "sessionId":"s", "update": {"sessionUpdate":"tool_call", "toolCallId":"t", "title":"Review", "status":"completed",
        "rawInput":{"secret":"input"}, "rawOutput":{"secret":"output"},
        "content":[{"type":"content","content":{"type":"text","text":"file bytes"}}]}
    }), "s", 128)?.ok_or_else(|| anyhow::anyhow!("tool title missing"))?;
    assert_eq!(item["title"], "Review");
    assert_eq!(item["status"], "completed");
    let encoded = serde_json::to_string(&item)?;
    for forbidden in ["rawInput", "rawOutput", "file bytes", "secret"] {
        assert!(!encoded.contains(forbidden));
    }
    Ok(())
}

#[test]
fn text_is_cut_at_a_character_boundary_and_marked() -> anyhow::Result<()> {
    let item = project(
        Version::One,
        json!({"sessionId":"s", "update":{
            "sessionUpdate":"agent_message_chunk", "content":{"type":"text","text":"é".repeat(128)}
        }}),
        "s",
        128,
    )?
    .ok_or_else(|| anyhow::anyhow!("chunk missing"))?;
    let text = item["text"]
        .as_str()
        .ok_or_else(|| anyhow::anyhow!("text missing"))?;
    assert!(text.chars().all(|ch| ch == 'é'));
    assert!(serde_json::to_vec(&item)?.len() <= 128);
    assert_eq!(item["truncated"], true);
    assert!(
        project(
            Version::One,
            json!({"sessionId":"foreign","update":{
                "sessionUpdate":"agent_message_chunk","content":{"type":"text","text":"private"}
            }}),
            "s",
            64
        )
        .is_err()
    );
    Ok(())
}

#[test]
fn v2_chunks_keep_the_required_message_identity() -> anyhow::Result<()> {
    let item = project(Version::Two, json!({"sessionId":"s","update":{
        "sessionUpdate":"agent_message_chunk","messageId":"message-1","content":{"type":"text","text":"response"}
    }}), "s", 256)?.ok_or_else(|| anyhow::anyhow!("v2 chunk missing"))?;
    assert_eq!(item["message_id"], "message-1");
    assert_eq!(item["text"], "response");
    Ok(())
}

/// AGT-007 R1: only a Lys seat whose launcher holds a pipe or a socket on
/// stdin watches it; an open seat, a file, /dev/null or no stdin never ends
/// the host by its input.
#[test]
fn only_a_lys_seat_on_a_pipe_or_socket_watches_its_input() -> anyhow::Result<()> {
    use super::host::watches_input;
    let kind = |fd: std::os::fd::OwnedFd| -> anyhow::Result<std::fs::FileType> {
        Ok(std::fs::File::from(fd).metadata()?.file_type())
    };
    let regular = tempfile::tempfile()?.metadata()?.file_type();
    let null = std::fs::metadata("/dev/null")?.file_type();
    let (reader, _writer) = std::io::pipe()?;
    let pipe = kind(reader.into())?;
    let (socket, _peer) = std::os::unix::net::UnixStream::pair()?;
    let socket = kind(socket.into())?;
    for stdin in [regular, null, pipe, socket] {
        assert!(!watches_input(false, Some(stdin)));
    }
    assert!(!watches_input(true, Some(regular)));
    assert!(!watches_input(true, Some(null)));
    assert!(!watches_input(true, None));
    assert!(watches_input(true, Some(pipe)));
    assert!(watches_input(true, Some(socket)));
    Ok(())
}