Skip to main content

agent_sdk_tools/
authority.rs

1//! Event envelope authority — governs how raw events are wrapped in
2//! [`AgentEventEnvelope`]s with unique IDs, sequence numbers, and timestamps.
3//!
4//! In local/CLI mode the SDK creates a [`LocalEventAuthority`] that starts
5//! sequencing at 0 for each run.  In server mode the orchestration layer
6//! injects its own authority (or seeds the offset) so ordering is continuous
7//! across turns within the same thread.
8
9use agent_sdk_foundation::events::{AgentEvent, AgentEventEnvelope, SequenceCounter};
10
11/// Authority that governs how events are wrapped in envelopes.
12///
13/// In local/CLI mode a fresh [`SequenceCounter`] starts at 0 per run.
14/// In server mode the authority seeds sequences from durable storage
15/// so ordering is continuous across turns within a thread.
16pub trait EventAuthority: Send + Sync {
17    /// Wrap a raw event into an authoritative envelope.
18    fn wrap(&self, event: AgentEvent) -> AgentEventEnvelope;
19}
20
21/// Default event authority for local/CLI execution.
22///
23/// Creates envelopes using a [`SequenceCounter`] that starts at 0 (or a
24/// caller-supplied offset).  This reproduces the existing behaviour — it is
25/// simply extracted behind the [`EventAuthority`] trait so the server can
26/// substitute its own implementation.
27pub struct LocalEventAuthority {
28    seq: SequenceCounter,
29}
30
31impl LocalEventAuthority {
32    /// Create an authority that starts sequencing at 0.
33    #[must_use]
34    pub fn new() -> Self {
35        Self {
36            seq: SequenceCounter::new(),
37        }
38    }
39
40    /// Create an authority that starts sequencing at the given offset.
41    ///
42    /// Used by server mode to resume sequencing where the previous turn
43    /// left off.
44    #[must_use]
45    pub fn with_offset(start: u64) -> Self {
46        Self {
47            seq: SequenceCounter::with_offset(start),
48        }
49    }
50}
51
52impl Default for LocalEventAuthority {
53    fn default() -> Self {
54        Self::new()
55    }
56}
57
58impl EventAuthority for LocalEventAuthority {
59    fn wrap(&self, event: AgentEvent) -> AgentEventEnvelope {
60        AgentEventEnvelope::wrap(event, &self.seq)
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67
68    fn sample_event() -> AgentEvent {
69        AgentEvent::text("msg_test", "hello")
70    }
71
72    #[test]
73    fn local_authority_sequences_from_zero() {
74        let auth = LocalEventAuthority::new();
75        let e0 = auth.wrap(sample_event());
76        let e1 = auth.wrap(sample_event());
77        let e2 = auth.wrap(sample_event());
78        assert_eq!(e0.sequence, 0);
79        assert_eq!(e1.sequence, 1);
80        assert_eq!(e2.sequence, 2);
81    }
82
83    #[test]
84    fn local_authority_with_offset_resumes_sequencing() {
85        let auth = LocalEventAuthority::with_offset(100);
86        let e0 = auth.wrap(sample_event());
87        let e1 = auth.wrap(sample_event());
88        assert_eq!(e0.sequence, 100);
89        assert_eq!(e1.sequence, 101);
90    }
91
92    #[test]
93    fn local_authority_default_same_as_new() {
94        let auth = LocalEventAuthority::default();
95        assert_eq!(auth.wrap(sample_event()).sequence, 0);
96    }
97
98    #[test]
99    fn local_authority_assigns_unique_event_ids() {
100        let auth = LocalEventAuthority::new();
101        let ids: Vec<_> = (0..100)
102            .map(|_| auth.wrap(sample_event()).event_id)
103            .collect();
104        let unique: std::collections::HashSet<_> = ids.iter().collect();
105        assert_eq!(ids.len(), unique.len());
106    }
107}