agent-phone 0.1.0

Minimal sync RPC between two AI agents (Rust port of @p-vbordei/agent-phone). Self-custody keys, Noise-framework handshake, DID-bound WebSocket.
Documentation
//! agent-phone quickstart: in-process server + client over loopback WS.
//!
//! Spins up a responder on 127.0.0.1 (ephemeral port), connects a client over
//! WebSocket, completes the Noise_XK handshake bound to both agents' DIDs,
//! runs one unary RPC, prints the result, and shuts everything down cleanly.
//!
//! Run with:
//!     cargo run --example quickstart

use agent_phone::session::HandlerOutput;
use agent_phone::{
    connect, create_server, encode_did_key, generate_key_pair, ClientOptions, Handler,
    ServerOptions,
};
use serde_json::Value;
use std::collections::HashMap;
use std::sync::Arc;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Two self-custody Ed25519 keypairs → two did:key identifiers.
    let responder = generate_key_pair();
    let initiator = generate_key_pair();
    let responder_did = encode_did_key(&responder.public_key)?;
    let initiator_did = encode_did_key(&initiator.public_key)?;

    // One handler: echo the params back as the result.
    let mut handlers: HashMap<String, Handler> = HashMap::new();
    handlers.insert(
        "echo".into(),
        Arc::new(|p: Value| Box::pin(async move { Ok(HandlerOutput::Unary(p)) })),
    );

    // Start a responder on an ephemeral loopback port.
    let mut server = create_server(ServerOptions {
        did: responder_did.clone(),
        private_key: responder.private_key,
        handlers,
    });
    server.listen(0, "127.0.0.1").await?;
    let port = server.address().expect("bound").port();
    println!("server listening on 127.0.0.1:{port}");

    // Dial the responder. The handshake binds the channel to both DIDs.
    let client = connect(ClientOptions {
        url: format!("ws://127.0.0.1:{port}"),
        did: initiator_did,
        private_key: initiator.private_key,
        responder_did: responder_did.clone(),
        responder_public_key: None,
    })
    .await?;
    println!("noise-xk handshake complete; channel authenticated");

    // One unary round-trip.
    let reply = client
        .call("echo", Some(serde_json::json!({"hello": "agent-phone"})))
        .await?;
    println!("echo result  : {reply}");
    assert_eq!(reply, serde_json::json!({"hello": "agent-phone"}));

    client.close().await;
    server.close().await;
    println!("closed cleanly");
    Ok(())
}