//! Mentions by part of a name (spec ยง10.3): `@kimi` reaches the one agent //! with that part in its name; a part two agents share reaches nobody. //! `@channel` reaches the agents who belong in the channel, `@all` every //! agent. mod common; use common::{run, setup, text, Serving}; use serde_json::{json, Value}; use spectra_core::keys::KeyPair; use spectra_mcp::client::{rpc_result, Client}; async fn call(c: &Client, name: &str, args: Value) -> Value { let req = json!({ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": name, "arguments": args } }); rpc_result(&c.mcp("2025-11-25", &req).await.unwrap()).unwrap()["result"].clone() } #[tokio::test] async fn a_unique_part_of_a_name_is_a_mention() { let dir = setup(); let d = dir.path(); for p in ["book", "web"] { assert!(run(d, &["project", "add", p]).status.success()); } let mut keys = std::collections::HashMap::new(); for (n, kind, p) in [("kimi-book", "kimi-code", "book"), ("claude-book", "claude-code", "book"), ("claude-web", "claude-code", "web")] { let home = d.join("agents").join(n); let o = run(d, &["agent", "add", n, "--kind", kind, "--project", p, "--home", home.to_str().unwrap(), "--yes"]); assert!(o.status.success(), "{}", text(&o)); keys.insert(n, KeyPair::load_home(&home).unwrap()); } let srv = Serving::start(d); let c = |n: &str| Client::new(&srv.socket, Some(keys[n].clone())); call(&c("claude-web"), "say", json!({ "body": "@Kimi and @WEB, and @book: hello", "channel": "general" })).await; let unread = |r: Value| r["structuredContent"]["items"].as_array().map(|a| a.len()).unwrap_or(0); assert_eq!(unread(call(&c("kimi-book"), "inbox", json!({})).await), 1, "@Kimi reaches kimi-book"); assert_eq!(unread(call(&c("claude-book"), "inbox", json!({})).await), 0, "@book is two agents: nobody"); // @channel: the agents who belong in #book; @all: everyone. assert!(run(d, &["agent", "allow", "claude-web", "--channel", "book"]).status.success()); call(&c("claude-web"), "say", json!({ "body": "@channel the index moved", "channel": "book" })).await; assert_eq!(unread(call(&c("claude-book"), "inbox", json!({})).await), 1, "@channel in #book reaches claude-book"); assert_eq!(unread(call(&c("kimi-book"), "inbox", json!({})).await), 2, "and kimi-book"); call(&c("kimi-book"), "say", json!({ "body": "@all heads up", "channel": "general" })).await; assert_eq!(unread(call(&c("claude-web"), "inbox", json!({})).await), 1, "@all reaches claude-web too"); srv.stop(); }