1use std::collections::VecDeque;
2
3use thiserror::Error;
4
5use crate::application::RequestId;
6use crate::folder::FolderState;
7use crate::terminal::{TerminalExit, TerminalId};
8use crate::workflow::{SessionId, Workflow, WorkflowId, WorkflowState};
9
10#[derive(Clone, Debug, Eq, PartialEq)]
11pub enum StateEvent {
12 ApplicationReady,
13 ConfigChanged(Box<crate::config::Config>),
14 WorkflowTypesChanged(Vec<crate::WorkflowType>),
15 TraceChanged(crate::TraceSummary),
16 WorkflowsChanged(WorkflowState),
17 WorkflowChanged(Workflow),
18 FoldersChanged(FolderState),
20 TerminalClosed {
21 terminal_id: TerminalId,
22 },
23 TerminalExited {
24 terminal_id: TerminalId,
25 exit: TerminalExit,
26 },
27 TerminalFailed {
28 terminal_id: TerminalId,
29 message: String,
30 },
31}
32
33#[derive(Clone, Debug, Eq, PartialEq)]
34pub enum CommandResult {
35 WorkflowTypeValidated { issues: Vec<crate::ValidationIssue> },
36 WorkflowTypeSaved { reference: crate::WorkflowTypeRef },
37 WorkflowTypeDeleted { reference: crate::WorkflowTypeRef },
38 SessionCreated { session_id: SessionId },
39 SessionRenamed { session_id: SessionId },
40 SessionSelected { session_id: SessionId },
41 SessionDeleted { session_id: SessionId },
42 Pong,
43 WorkflowCreated { workflow_id: WorkflowId },
44 WorkflowActivated { workflow_id: WorkflowId },
45 WorkflowClosed { workflow_id: WorkflowId },
46 AgentStarted { workflow_id: WorkflowId },
47 AgentCancelled { workflow_id: WorkflowId },
48 TerminalStarted { terminal_id: TerminalId },
49 TerminalClosed { terminal_id: TerminalId },
50}
51
52#[derive(Clone, Debug, Eq, PartialEq)]
53pub enum EventKind {
54 State(StateEvent),
55 CommandCompleted {
56 request_id: RequestId,
57 result: CommandResult,
58 },
59}
60
61#[derive(Clone, Debug, Eq, PartialEq)]
62pub struct Event {
63 pub sequence: u64,
64 pub kind: EventKind,
65}
66
67#[derive(Debug)]
68pub(crate) struct EventJournal {
69 capacity: usize,
70 events: VecDeque<Event>,
71 latest_sequence: u64,
72}
73
74impl EventJournal {
75 pub(crate) fn new(capacity: usize) -> Result<Self, EventError> {
76 if capacity == 0 {
77 return Err(EventError::ZeroCapacity);
78 }
79
80 Ok(Self {
81 capacity,
82 events: VecDeque::with_capacity(capacity),
83 latest_sequence: 0,
84 })
85 }
86
87 pub(crate) fn append(&mut self, kind: EventKind) -> Result<Event, EventError> {
88 let sequence = self
89 .latest_sequence
90 .checked_add(1)
91 .ok_or(EventError::SequenceOverflow)?;
92 let event = Event { sequence, kind };
93 self.events.push_back(event.clone());
94 self.latest_sequence = sequence;
95
96 if self.events.len() > self.capacity {
97 self.events.pop_front();
98 }
99
100 Ok(event)
101 }
102
103 pub(crate) const fn latest_sequence(&self) -> u64 {
104 self.latest_sequence
105 }
106
107 pub(crate) fn after(&self, sequence: u64, limit: usize) -> Result<Vec<Event>, EventError> {
108 if limit == 0 {
109 return Err(EventError::ZeroLimit);
110 }
111
112 if let Some(oldest) = self.events.front() {
113 let oldest_cursor = oldest.sequence.saturating_sub(1);
114 if sequence < oldest_cursor {
115 return Err(EventError::CursorExpired {
116 requested: sequence,
117 oldest_available: oldest.sequence,
118 });
119 }
120 }
121
122 Ok(self
123 .events
124 .iter()
125 .filter(|event| event.sequence > sequence)
126 .take(limit)
127 .cloned()
128 .collect())
129 }
130}
131
132#[derive(Debug, Error)]
133pub enum EventError {
134 #[error(
135 "event cursor {requested} has expired; the oldest available event is {oldest_available}"
136 )]
137 CursorExpired {
138 requested: u64,
139 oldest_available: u64,
140 },
141 #[error("event sequence overflowed")]
142 SequenceOverflow,
143 #[error("event journal capacity must be greater than zero")]
144 ZeroCapacity,
145 #[error("event batch limit must be greater than zero")]
146 ZeroLimit,
147}