Skip to main content

af_agent/
inbox.rs

1use std::collections::VecDeque;
2
3use af_agent_session::{ContentBlock, DeliveryMode};
4
5#[derive(Debug, Clone, PartialEq)]
6pub struct InboxItem {
7    pub input_id: String,
8    pub mode: DeliveryMode,
9    pub content: Vec<ContentBlock>,
10}
11
12#[derive(Debug, Default)]
13pub struct Inbox {
14    queued: VecDeque<InboxItem>,
15}
16
17impl Inbox {
18    pub fn push(&mut self, item: InboxItem, run_active: bool) -> Result<(), InboxError> {
19        if !run_active && item.mode != DeliveryMode::Followup {
20            return Err(InboxError::NoActiveRun);
21        }
22        match item.mode {
23            DeliveryMode::Followup => self.queued.push_back(item),
24            DeliveryMode::Steer => self.queued.push_front(item),
25            DeliveryMode::Inject => {
26                while self
27                    .queued
28                    .front()
29                    .is_some_and(|queued| queued.mode == DeliveryMode::Inject)
30                {
31                    self.queued.pop_front();
32                }
33                self.queued.push_front(item);
34            }
35        }
36        Ok(())
37    }
38
39    pub fn pop(&mut self) -> Option<InboxItem> {
40        self.queued.pop_front()
41    }
42    pub fn is_empty(&self) -> bool {
43        self.queued.is_empty()
44    }
45}
46
47#[derive(Debug, thiserror::Error, PartialEq, Eq)]
48pub enum InboxError {
49    #[error("steer and inject require an active run")]
50    NoActiveRun,
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56    use af_agent_session::text;
57
58    fn item(id: &str, mode: DeliveryMode) -> InboxItem {
59        InboxItem {
60            input_id: id.into(),
61            mode,
62            content: text(id),
63        }
64    }
65
66    #[test]
67    fn inject_preempts_and_coalesces() {
68        let mut inbox = Inbox::default();
69        inbox
70            .push(item("later", DeliveryMode::Followup), true)
71            .unwrap();
72        inbox.push(item("old", DeliveryMode::Inject), true).unwrap();
73        inbox.push(item("new", DeliveryMode::Inject), true).unwrap();
74        assert_eq!(inbox.pop().unwrap().input_id, "new");
75        assert_eq!(inbox.pop().unwrap().input_id, "later");
76    }
77}