//! Where decided `recall` and `memory` calls go: to `memoryd` over its `brokerd` socket, with the //! session's state, instead of a container (M5a spec, section 8). `Routed` sends every other call //! to the configured runtime. use std::os::unix::net::UnixStream; use std::path::PathBuf; use std::time::Duration; use bxcore::{ Envelope, MemoryCall, Message, PROTOCOL_VERSION, ToolRequest, ToolResponse, read_frame, write_frame, }; use crate::policy::Label; use crate::runner::{RunError, RunOutput, RunSpec, Runtime}; /// The sentence when `memoryd` cannot be reached or answers wrongly: fixed, it reaches the model. pub const UNREACHABLE: &str = "memory is unavailable: memoryd did not answer (see docs/runbook.md#memoryd-unreachable)"; /// How long `brokerd` waits for `memoryd` to answer one call. pub const MEMORY_WAIT: Duration = Duration::from_secs(30); /// A client of `memoryd`'s `brokerd` socket. One connection per call. pub struct MemoryPort { socket: PathBuf, wait: Duration, } impl MemoryPort { pub fn new(socket: PathBuf, wait: Duration) -> MemoryPort { MemoryPort { socket, wait } } fn ask(&self, spec: &RunSpec) -> Option { // `None` at the first thing that fails. 1. A MemoryCall: request with the spec's session, // call, `spec.tool().as_str()`, and `spec.arguments().canonical_json()`; taint and // untrusted from `spec.state()`. 2. `UnixStream::connect(&self.socket)`, then read and // write timeouts of `self.wait`. 3. `write_frame` an Envelope { v: PROTOCOL_VERSION, id: // spec.call().0, final: true, msg: Message::MemoryCall(call) }. 4. `read_frame`; a // Message::ToolResponse is Some(it), any other message None. let call = MemoryCall { request: ToolRequest { session: spec.session().clone(), call: spec.call(), tool: spec.tool().as_str().to_string(), arguments: spec.arguments().canonical_json(), }, taint: spec.state().taint, untrusted: spec.state().untrusted, }; let mut stream = UnixStream::connect(&self.socket).ok()?; stream.set_read_timeout(Some(self.wait)).ok()?; stream.set_write_timeout(Some(self.wait)).ok()?; write_frame( &mut stream, &Envelope { v: PROTOCOL_VERSION, id: spec.call().0, r#final: true, msg: Message::MemoryCall(call), }, ) .ok()?; let env = read_frame(&mut stream).ok()?; match env.msg { Message::ToolResponse(response) => Some(response), _ => None, } } } impl Runtime for MemoryPort { fn run(&self, spec: &RunSpec) -> Result { self.run_labelled(spec).map(|(output, _)| output) } fn run_labelled(&self, spec: &RunSpec) -> Result<(RunOutput, Option