Skip to main content

thndrs_agent/
run.rs

1//! Provider-neutral background-run ownership.
2
3use std::sync::mpsc::{self, Receiver, Sender};
4use std::thread;
5
6use crate::CancelToken;
7
8/// A running agent turn exposed through a semantic event stream.
9///
10/// The application supplies the closure that performs provider requests and
11/// tool execution. This type owns only the background thread, event channel,
12/// and cooperative cancellation handle.
13#[derive(Debug)]
14pub struct AgentRun<Event> {
15    events: Receiver<Event>,
16    cancel: CancelToken,
17}
18
19impl<Event: Send + 'static> AgentRun<Event> {
20    /// Start a provider-neutral run on a background thread.
21    pub fn spawn(cancel: CancelToken, run: impl FnOnce(Sender<Event>, CancelToken) + Send + 'static) -> Self {
22        let thread_cancel = cancel.clone();
23        let (sender, events) = mpsc::channel();
24        thread::spawn(move || run(sender, thread_cancel));
25        Self { events, cancel }
26    }
27
28    /// Consume the run and return its event receiver.
29    pub fn into_events(self) -> Receiver<Event> {
30        self.events
31    }
32
33    /// Return the cooperative cancellation handle for this run.
34    pub fn cancel(&self) -> &CancelToken {
35        &self.cancel
36    }
37}
38
39#[cfg(test)]
40mod tests {
41    use super::*;
42
43    #[test]
44    fn run_exposes_events_and_the_shared_cancel_token() {
45        let cancel = CancelToken::new();
46        let run = AgentRun::spawn(cancel, |sender, received_cancel| {
47            sender.send(received_cancel.is_cancelled()).expect("send event");
48        });
49
50        assert!(!run.cancel().is_cancelled());
51        assert!(!run.into_events().recv().expect("receive event"));
52    }
53}