liminal-cli 0.3.21

The liminal command: serve the network, maintain its stores, or serve its discovery MCP over stdio
Documentation
//! ACCESS-013 R2 and R4: an open exchange (no `[auth]`) with a top-level
//! `[agents]` gives a seat that declares a name its own station: its
//! `liminal mcp` start listens `agents.<name>.inbox` with no "not listened"
//! line, and another named seat's send to that inbox is delivered to it.

use std::error::Error;

use serde_json::json;

use super::network::OpenServer;
use super::open_services::seat_init;
use super::{Host, Ticket};

/// The registry's table, with the channel bound every station's inbox needs.
const AGENTS: &str = "[agents]\n\n[limits]\nmax_channels = 64\n";

#[test]
fn access013_an_open_seat_listens_its_own_inbox_and_another_seat_reaches_it()
-> Result<(), Box<dyn Error>> {
    let dir = tempfile::tempdir()?;
    let server = OpenServer::start_on_with(dir.path(), "127.0.0.1", AGENTS)?;
    let address = server.address.to_string();
    let hermes_seat = dir.path().join("hermes.toml");
    seat_init(&address, Some("hermes"), &hermes_seat)?;
    let other_seat = dir.path().join("other.toml");
    seat_init(&address, Some("other"), &other_seat)?;

    let mut hermes = Host::open(Ticket::spawn(&hermes_seat)?, true)?;
    let mut other = Host::open(Ticket::spawn(&other_seat)?, true)?;
    let text = "{\"text\":\"other to hermes\"}";
    let sent = other.tool(
        "send",
        &json!({"subject": "agents.hermes.inbox", "text": text, "key": "other-1"}),
    )?;
    assert_eq!(sent["isError"], false, "the send to hermes's inbox: {sent}");
    let received = hermes.ticket.read_line()?;
    assert_eq!(
        received["method"], "notifications/claude/channel",
        "{received}"
    );
    assert_eq!(received["params"]["content"], text, "{received}");
    assert_eq!(
        received["params"]["meta"]["subject"], "agents.hermes.inbox",
        "{received}"
    );

    let (stderr, code) = hermes.ticket.close()?;
    assert_eq!(code, 0, "{stderr}");
    assert!(!stderr.contains("is not listened"), "{stderr}");
    // ACCESS-013 R3: a station for another name says who places it.
    let refused = other.tool("listen", &json!({"subject": "agents.zz.presence"}))?;
    assert_eq!(refused["isError"], true, "{refused}");
    assert!(
        refused.to_string().contains(
            "unknown_agent: zz; a station for another name is placed by a person \
             (POST /admin/agents/create) or an approved draft"
        ),
        "{refused}"
    );
    let (stderr, code) = other.ticket.close()?;
    assert_eq!(code, 0, "{stderr}");
    assert!(!stderr.contains("is not listened"), "{stderr}");

    // A seat that declares no name has no station, said by name.
    let anonymous_seat = dir.path().join("anonymous.toml");
    seat_init(&address, None, &anonymous_seat)?;
    let mut anonymous = Host::open(Ticket::spawn(&anonymous_seat)?, true)?;
    let refused = anonymous.tool("listen", &json!({"subject": "agents.anonymous.inbox"}))?;
    assert_eq!(refused["isError"], true, "{refused}");
    assert!(
        refused
            .to_string()
            .contains("agent_invalid: an anonymous seat has no station; write [open] name"),
        "{refused}"
    );
    let (stderr, code) = anonymous.ticket.close()?;
    assert_eq!(code, 0, "{stderr}");
    Ok(())
}

/// ACCESS-013 R2 and R3: with no `[agents]` anywhere an open exchange boots as
/// before, and a named seat's inbox is refused in words that name the table.
#[test]
fn access013_an_open_exchange_with_no_registry_names_the_table_to_write()
-> Result<(), Box<dyn Error>> {
    let dir = tempfile::tempdir()?;
    let server = OpenServer::start_on_with(dir.path(), "127.0.0.1", "")?;
    let seat = dir.path().join("hermes.toml");
    seat_init(&server.address.to_string(), Some("hermes"), &seat)?;
    let mut hermes = Host::open(Ticket::spawn(&seat)?, true)?;
    let listed = hermes.tool("services", &json!({}))?;
    assert!(listed.get("isError").is_some(), "{listed}");
    let (stderr, code) = hermes.ticket.close()?;
    assert_eq!(code, 0, "{stderr}");
    assert!(
        stderr.contains("liminal mcp: agents.hermes.inbox is not listened: ")
            && stderr.contains(
                "agent_invalid: no agent registry; write [agents] (or [auth.lys.agents] on a Lys \
                 exchange)"
            ),
        "{stderr}"
    );
    Ok(())
}