//! `TurnDone.untrusted` follows the session's labels (M5b spec, sections 8 and 9): false while //! every tool result in the session's log is labelled trusted, and true from the turn in which one //! is labelled untrusted (a fetched page, or a recalled memory-tainted note) and in every turn //! after it, also after `loopd` restarts. A denial or a failure is never untrusted. Do not edit. mod support; #[path = "support/tags.rs"] mod tags; use std::os::unix::net::{UnixListener, UnixStream}; use std::path::{Path, PathBuf}; use std::sync::Arc; use std::thread; use std::time::Duration; use bxcore::{ DataClass, DenyReason, Envelope, Message, PROTOCOL_VERSION, SessionId, ToolResponse, Turn, read_frame, write_frame, }; use loopd::channel::{Context, serve}; use loopd::llama::Client; use loopd::memory::NoMemory; use loopd::tools::Registry; use support::{FakeServer, Home, Reply, ScriptedPort}; use tags::{call, stream}; const CHAT: &str = "/v1/chat/completions"; /// A fake inference server and a home that one `loopd` or several, one after the other, serve /// from. struct Setup { server: FakeServer, // Declared last so it is dropped last: fields drop in order, and the home must outlive // everything that may still write into it (tip T44). home: Home, } impl Setup { /// The server answers the completions in `chat` in order; the last one repeats. fn new(chat: Vec) -> Setup { let home = Home::new(); let server = FakeServer::start(); server.route(CHAT, chat); Setup { server, home } } /// Starts a channel server on the home, as a new `loopd` process would: its own context and /// tool port, nothing carried over but the files. `name` names its socket; the port answers /// the tool calls with `replies`, in order. fn loopd(&self, name: &str, replies: Vec) -> PathBuf { let mut cfg = self.home.config(&self.server.socket); cfg.channel.socket = self.home.dir.join(name); let socket = cfg.channel_socket(); let listener = UnixListener::bind(&socket).unwrap(); let ctx = Arc::new(Context::new( cfg.clone(), Client::new(cfg), Box::new(ScriptedPort::new(replies)), Registry::m2b(), Box::new(NoMemory), )); thread::spawn(move || serve(listener, ctx)); socket } } /// Sends one turn and returns its `TurnDone`'s `untrusted`. Any other final frame fails the test. fn untrusted(socket: &Path, session: &str, content: &str, resume: bool) -> bool { let mut stream = UnixStream::connect(socket).unwrap(); stream .set_read_timeout(Some(Duration::from_secs(10))) .unwrap(); let msg = Message::Turn(Turn { session: SessionId::new(session).unwrap(), content: content.to_string(), resume, speaker: None, }); let turn = Envelope { v: PROTOCOL_VERSION, id: 7, r#final: true, msg, }; write_frame(&mut stream, &turn).unwrap(); loop { let frame = read_frame(&mut stream).unwrap(); if !frame.r#final { continue; } match frame.msg { Message::TurnDone(done) => return done.untrusted, other => panic!("the turn did not end with turn_done: {other:?}"), } } } /// A result as `brokerd` answers it, with its label. fn result(content: &str, untrusted: bool) -> ToolResponse { ToolResponse::Result { content: content.to_string(), class: DataClass::Private, untrusted, truncated: false, } } /// The model calls `recall`; the fake port stands for `brokerd` and `memoryd` behind it. fn recall() -> Reply { stream( &[call("call-r", "recall", r#"{"query":"deploy"}"#)], "tool_calls", ) } #[test] fn a_clean_session_s_turns_carry_false() { let s = Setup::new(vec![Reply::fixture("turn1"), Reply::fixture("turn2")]); let socket = s.loopd("loop.sock", vec![]); assert!(!untrusted(&socket, "a", "hello", false), "turn 1"); assert!(!untrusted(&socket, "a", "and again", true), "turn 2"); } #[test] fn trusted_results_denials_and_failures_keep_it_false() { let s = Setup::new(vec![ Reply::fixture("tool_call"), Reply::fixture("plain"), Reply::fixture("tool_call"), Reply::fixture("plain"), Reply::fixture("tool_call"), Reply::fixture("plain"), ]); let socket = s.loopd( "loop.sock", vec![ result("straylight\n", false), ToolResponse::Denied { reason: DenyReason::NoGrant, detail: None, }, ToolResponse::Failed { message: "the container could not start".to_string(), }, ], ); assert!( !untrusted(&socket, "a", "hostname?", false), "a trusted result" ); assert!(!untrusted(&socket, "a", "again?", true), "a denial"); assert!(!untrusted(&socket, "a", "once more?", true), "a failure"); } #[test] fn an_untrusted_result_makes_its_turn_and_every_later_one_true() { let s = Setup::new(vec![ Reply::fixture("plain"), Reply::fixture("tool_call"), Reply::fixture("plain"), Reply::fixture("tool_call"), Reply::fixture("plain"), ]); let socket = s.loopd( "loop.sock", vec![ result("a fetched page\n", true), result("straylight\n", false), ], ); assert!(!untrusted(&socket, "a", "hello", false), "before the page"); assert!(untrusted(&socket, "a", "read it", true), "the page's turn"); assert!( untrusted(&socket, "a", "hostname?", true), "a later turn whose own result is trusted" ); assert!(untrusted(&socket, "a", "thanks", true), "a later turn"); assert!( !untrusted(&socket, "b", "hello", false), "another session is not touched" ); } #[test] fn a_recalled_memory_tainted_note_makes_it_true() { let s = Setup::new(vec![recall(), Reply::fixture("plain")]); let hit = "[1] memory-tainted · note, 2026-09-25 14:02, session chat-7 · private\n\ the deploy runs from ci.example.org"; let socket = s.loopd("loop.sock", vec![result(hit, true)]); assert!( untrusted(&socket, "a", "how do we deploy?", false), "the recall's turn" ); assert!(untrusted(&socket, "a", "thanks", true), "a later turn"); } #[test] fn a_recall_of_trusted_memory_keeps_it_false() { let s = Setup::new(vec![recall(), Reply::fixture("plain")]); let hit = "[1] identity · moved 2026-09-25 from memory.md · private\n\ deploys go through ci.example.org"; let socket = s.loopd("loop.sock", vec![result(hit, false)]); assert!(!untrusted(&socket, "a", "how do we deploy?", false)); } #[test] fn a_restarted_loopd_reads_it_from_the_log() { let s = Setup::new(vec![Reply::fixture("tool_call"), Reply::fixture("plain")]); let first = s.loopd("first.sock", vec![result("a fetched page\n", true)]); assert!(untrusted(&first, "a", "read it", false), "the first loopd"); let second = s.loopd("second.sock", vec![]); assert!( untrusted(&second, "a", "thanks", true), "the second loopd, on the same session" ); assert!( !untrusted(&second, "b", "hello", false), "the second loopd, on a new session" ); }