Skip to main content

syncable_ag_ui_core/
event.rs

1//! AG-UI Event Types
2//!
3//! This module defines all AG-UI protocol event types including:
4//! - Text message events (start, content, end, chunk)
5//! - Thinking text message events
6//! - Tool call events (start, args, end, result)
7//! - State events (snapshot, delta)
8//! - Run lifecycle events (started, finished, error)
9//! - Step events (started, finished)
10//! - Custom and raw events
11
12use crate::state::AgentState;
13use crate::types::{Message, MessageId, Role, RunId, ThreadId, ToolCallId};
14use crate::JsonValue;
15use serde::{Deserialize, Serialize};
16
17/// Event types for the AG-UI protocol.
18///
19/// This enum enumerates all possible event types in the protocol.
20/// Event types are serialized using SCREAMING_SNAKE_CASE (e.g., `TEXT_MESSAGE_START`).
21///
22/// # Event Categories
23///
24/// - **Text Messages**: `TextMessageStart`, `TextMessageContent`, `TextMessageEnd`, `TextMessageChunk`
25/// - **Thinking Messages**: `ThinkingTextMessageStart`, `ThinkingTextMessageContent`, `ThinkingTextMessageEnd`
26/// - **Tool Calls**: `ToolCallStart`, `ToolCallArgs`, `ToolCallEnd`, `ToolCallChunk`, `ToolCallResult`
27/// - **Thinking Steps**: `ThinkingStart`, `ThinkingEnd`
28/// - **State**: `StateSnapshot`, `StateDelta`
29/// - **Messages**: `MessagesSnapshot`
30/// - **Run Lifecycle**: `RunStarted`, `RunFinished`, `RunError`
31/// - **Step Lifecycle**: `StepStarted`, `StepFinished`
32/// - **Other**: `Raw`, `Custom`
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
34#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
35pub enum EventType {
36    /// Start of a text message from the assistant.
37    TextMessageStart,
38    /// Content chunk of a text message (streaming delta).
39    TextMessageContent,
40    /// End of a text message.
41    TextMessageEnd,
42    /// Complete text message chunk (non-streaming alternative).
43    TextMessageChunk,
44    /// Start of a thinking text message (extended thinking).
45    ThinkingTextMessageStart,
46    /// Content chunk of a thinking text message.
47    ThinkingTextMessageContent,
48    /// End of a thinking text message.
49    ThinkingTextMessageEnd,
50    /// Start of a tool call.
51    ToolCallStart,
52    /// Arguments chunk for a tool call (streaming).
53    ToolCallArgs,
54    /// End of a tool call.
55    ToolCallEnd,
56    /// Complete tool call chunk (non-streaming alternative).
57    ToolCallChunk,
58    /// Result of a tool call execution.
59    ToolCallResult,
60    /// Start of a thinking step (chain-of-thought).
61    ThinkingStart,
62    /// End of a thinking step.
63    ThinkingEnd,
64    /// Complete state snapshot.
65    StateSnapshot,
66    /// Incremental state update (JSON Patch RFC 6902).
67    StateDelta,
68    /// Complete messages snapshot.
69    MessagesSnapshot,
70    /// Complete activity snapshot.
71    ActivitySnapshot,
72    /// Incremental activity update (JSON Patch RFC 6902).
73    ActivityDelta,
74    /// Raw event from the underlying provider.
75    Raw,
76    /// Custom application-specific event.
77    Custom,
78    /// Agent run has started.
79    RunStarted,
80    /// Agent run has finished successfully.
81    RunFinished,
82    /// Agent run encountered an error.
83    RunError,
84    /// A step within a run has started.
85    StepStarted,
86    /// A step within a run has finished.
87    StepFinished,
88}
89
90impl EventType {
91    /// Returns the string representation of the event type.
92    pub fn as_str(&self) -> &'static str {
93        match self {
94            EventType::TextMessageStart => "TEXT_MESSAGE_START",
95            EventType::TextMessageContent => "TEXT_MESSAGE_CONTENT",
96            EventType::TextMessageEnd => "TEXT_MESSAGE_END",
97            EventType::TextMessageChunk => "TEXT_MESSAGE_CHUNK",
98            EventType::ThinkingTextMessageStart => "THINKING_TEXT_MESSAGE_START",
99            EventType::ThinkingTextMessageContent => "THINKING_TEXT_MESSAGE_CONTENT",
100            EventType::ThinkingTextMessageEnd => "THINKING_TEXT_MESSAGE_END",
101            EventType::ToolCallStart => "TOOL_CALL_START",
102            EventType::ToolCallArgs => "TOOL_CALL_ARGS",
103            EventType::ToolCallEnd => "TOOL_CALL_END",
104            EventType::ToolCallChunk => "TOOL_CALL_CHUNK",
105            EventType::ToolCallResult => "TOOL_CALL_RESULT",
106            EventType::ThinkingStart => "THINKING_START",
107            EventType::ThinkingEnd => "THINKING_END",
108            EventType::StateSnapshot => "STATE_SNAPSHOT",
109            EventType::StateDelta => "STATE_DELTA",
110            EventType::MessagesSnapshot => "MESSAGES_SNAPSHOT",
111            EventType::ActivitySnapshot => "ACTIVITY_SNAPSHOT",
112            EventType::ActivityDelta => "ACTIVITY_DELTA",
113            EventType::Raw => "RAW",
114            EventType::Custom => "CUSTOM",
115            EventType::RunStarted => "RUN_STARTED",
116            EventType::RunFinished => "RUN_FINISHED",
117            EventType::RunError => "RUN_ERROR",
118            EventType::StepStarted => "STEP_STARTED",
119            EventType::StepFinished => "STEP_FINISHED",
120        }
121    }
122}
123
124impl std::fmt::Display for EventType {
125    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
126        write!(f, "{}", self.as_str())
127    }
128}
129
130/// Base event structure for all AG-UI protocol events.
131///
132/// Contains common fields that are present in all event types.
133/// Individual event structs flatten this into their structure.
134///
135/// # Fields
136///
137/// - `timestamp`: Optional Unix timestamp in milliseconds since epoch
138/// - `raw_event`: Optional raw event from the underlying provider (for debugging/passthrough)
139///
140/// # Example
141///
142/// ```rust
143/// use ag_ui_core::event::BaseEvent;
144///
145/// let base = BaseEvent {
146///     timestamp: Some(1706123456789.0),
147///     raw_event: None,
148/// };
149/// ```
150#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
151pub struct BaseEvent {
152    /// Unix timestamp in milliseconds since epoch.
153    #[serde(skip_serializing_if = "Option::is_none")]
154    pub timestamp: Option<f64>,
155    /// Raw event from the underlying provider (for debugging/passthrough).
156    #[serde(rename = "rawEvent", skip_serializing_if = "Option::is_none")]
157    pub raw_event: Option<JsonValue>,
158}
159
160impl BaseEvent {
161    /// Creates a new empty BaseEvent.
162    pub fn new() -> Self {
163        Self::default()
164    }
165
166    /// Creates a BaseEvent with the current timestamp.
167    pub fn with_current_timestamp() -> Self {
168        Self {
169            timestamp: Some(
170                std::time::SystemTime::now()
171                    .duration_since(std::time::UNIX_EPOCH)
172                    .map(|d| d.as_millis() as f64)
173                    .unwrap_or(0.0),
174            ),
175            raw_event: None,
176        }
177    }
178
179    /// Sets the timestamp for this event.
180    pub fn timestamp(mut self, timestamp: f64) -> Self {
181        self.timestamp = Some(timestamp);
182        self
183    }
184
185    /// Sets the raw event for this event.
186    pub fn raw_event(mut self, raw_event: JsonValue) -> Self {
187        self.raw_event = Some(raw_event);
188        self
189    }
190}
191
192/// Validation errors for AG-UI protocol events.
193///
194/// These errors occur when event data fails validation rules.
195#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
196pub enum EventValidationError {
197    /// Delta content must not be empty.
198    #[error("Delta must not be an empty string")]
199    EmptyDelta,
200    /// Event format is invalid.
201    #[error("Invalid event format: {0}")]
202    InvalidFormat(String),
203    /// Required field is missing.
204    #[error("Missing required field: {0}")]
205    MissingField(String),
206    /// Event type mismatch.
207    #[error("Event type mismatch: expected {expected}, got {actual}")]
208    TypeMismatch {
209        /// Expected event type.
210        expected: String,
211        /// Actual event type.
212        actual: String,
213    },
214}
215
216// =============================================================================
217// Text Message Events
218// =============================================================================
219
220/// Event indicating the start of a text message.
221///
222/// This event is sent when the agent begins generating a text message.
223/// The message_id identifies this message throughout the streaming process.
224///
225/// # Example
226///
227/// ```rust
228/// use ag_ui_core::{MessageId, Role};
229/// use ag_ui_core::event::TextMessageStartEvent;
230///
231/// let event = TextMessageStartEvent::new(MessageId::random());
232/// assert_eq!(event.role, Role::Assistant);
233/// ```
234#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
235pub struct TextMessageStartEvent {
236    /// Common event fields (timestamp, rawEvent).
237    #[serde(flatten)]
238    pub base: BaseEvent,
239    /// Unique identifier for this message.
240    #[serde(rename = "messageId")]
241    pub message_id: MessageId,
242    /// The role of the message sender (always Assistant for new messages).
243    pub role: Role,
244}
245
246impl TextMessageStartEvent {
247    /// Creates a new TextMessageStartEvent with the given message ID.
248    pub fn new(message_id: impl Into<MessageId>) -> Self {
249        Self {
250            base: BaseEvent::default(),
251            message_id: message_id.into(),
252            role: Role::Assistant,
253        }
254    }
255
256    /// Sets the timestamp for this event.
257    pub fn with_timestamp(mut self, timestamp: f64) -> Self {
258        self.base.timestamp = Some(timestamp);
259        self
260    }
261
262    /// Sets the raw event for this event.
263    pub fn with_raw_event(mut self, raw_event: JsonValue) -> Self {
264        self.base.raw_event = Some(raw_event);
265        self
266    }
267}
268
269/// Event containing a piece of text message content.
270///
271/// This event is sent for each chunk of content as the agent generates a message.
272/// The delta field contains the new text to append to the message.
273///
274/// # Validation
275///
276/// The delta must not be empty. Use `new()` which returns a Result to validate,
277/// or `new_unchecked()` if you've already validated the input.
278#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
279pub struct TextMessageContentEvent {
280    /// Common event fields (timestamp, rawEvent).
281    #[serde(flatten)]
282    pub base: BaseEvent,
283    /// The message ID this content belongs to.
284    #[serde(rename = "messageId")]
285    pub message_id: MessageId,
286    /// The text content delta to append.
287    pub delta: String,
288}
289
290impl TextMessageContentEvent {
291    /// Creates a new TextMessageContentEvent with validation.
292    ///
293    /// Returns an error if delta is empty.
294    pub fn new(
295        message_id: impl Into<MessageId>,
296        delta: impl Into<String>,
297    ) -> Result<Self, EventValidationError> {
298        let delta = delta.into();
299        if delta.is_empty() {
300            return Err(EventValidationError::EmptyDelta);
301        }
302        Ok(Self {
303            base: BaseEvent::default(),
304            message_id: message_id.into(),
305            delta,
306        })
307    }
308
309    /// Creates a new TextMessageContentEvent without validation.
310    ///
311    /// Use this only if you've already validated the delta is not empty.
312    pub fn new_unchecked(message_id: impl Into<MessageId>, delta: impl Into<String>) -> Self {
313        Self {
314            base: BaseEvent::default(),
315            message_id: message_id.into(),
316            delta: delta.into(),
317        }
318    }
319
320    /// Validates this event's data.
321    pub fn validate(&self) -> Result<(), EventValidationError> {
322        if self.delta.is_empty() {
323            return Err(EventValidationError::EmptyDelta);
324        }
325        Ok(())
326    }
327
328    /// Sets the timestamp for this event.
329    pub fn with_timestamp(mut self, timestamp: f64) -> Self {
330        self.base.timestamp = Some(timestamp);
331        self
332    }
333}
334
335/// Event indicating the end of a text message.
336///
337/// This event is sent when the agent completes a text message.
338#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
339pub struct TextMessageEndEvent {
340    /// Common event fields (timestamp, rawEvent).
341    #[serde(flatten)]
342    pub base: BaseEvent,
343    /// The message ID that has completed.
344    #[serde(rename = "messageId")]
345    pub message_id: MessageId,
346}
347
348impl TextMessageEndEvent {
349    /// Creates a new TextMessageEndEvent.
350    pub fn new(message_id: impl Into<MessageId>) -> Self {
351        Self {
352            base: BaseEvent::default(),
353            message_id: message_id.into(),
354        }
355    }
356
357    /// Sets the timestamp for this event.
358    pub fn with_timestamp(mut self, timestamp: f64) -> Self {
359        self.base.timestamp = Some(timestamp);
360        self
361    }
362}
363
364/// Event containing a chunk of text message content.
365///
366/// This event combines start, content, and potentially end information in a single event.
367/// Used as a non-streaming alternative where all fields may be optional.
368#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
369pub struct TextMessageChunkEvent {
370    /// Common event fields (timestamp, rawEvent).
371    #[serde(flatten)]
372    pub base: BaseEvent,
373    /// Optional message ID (may be omitted for continuation chunks).
374    #[serde(rename = "messageId", skip_serializing_if = "Option::is_none")]
375    pub message_id: Option<MessageId>,
376    /// The role of the message sender.
377    pub role: Role,
378    /// Optional text content delta.
379    #[serde(skip_serializing_if = "Option::is_none")]
380    pub delta: Option<String>,
381}
382
383impl TextMessageChunkEvent {
384    /// Creates a new TextMessageChunkEvent with the given role.
385    pub fn new(role: Role) -> Self {
386        Self {
387            base: BaseEvent::default(),
388            message_id: None,
389            role,
390            delta: None,
391        }
392    }
393
394    /// Sets the message ID for this event.
395    pub fn with_message_id(mut self, message_id: impl Into<MessageId>) -> Self {
396        self.message_id = Some(message_id.into());
397        self
398    }
399
400    /// Sets the delta for this event.
401    pub fn with_delta(mut self, delta: impl Into<String>) -> Self {
402        self.delta = Some(delta.into());
403        self
404    }
405
406    /// Sets the timestamp for this event.
407    pub fn with_timestamp(mut self, timestamp: f64) -> Self {
408        self.base.timestamp = Some(timestamp);
409        self
410    }
411}
412
413// =============================================================================
414// Thinking Text Message Events
415// =============================================================================
416
417/// Event indicating the start of a thinking text message.
418///
419/// This event is sent when the agent begins generating internal thinking content
420/// (extended thinking / chain-of-thought). Unlike regular messages, thinking
421/// messages don't have a message ID as they're ephemeral.
422#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
423pub struct ThinkingTextMessageStartEvent {
424    /// Common event fields (timestamp, rawEvent).
425    #[serde(flatten)]
426    pub base: BaseEvent,
427}
428
429impl ThinkingTextMessageStartEvent {
430    /// Creates a new ThinkingTextMessageStartEvent.
431    pub fn new() -> Self {
432        Self {
433            base: BaseEvent::default(),
434        }
435    }
436
437    /// Sets the timestamp for this event.
438    pub fn with_timestamp(mut self, timestamp: f64) -> Self {
439        self.base.timestamp = Some(timestamp);
440        self
441    }
442}
443
444impl Default for ThinkingTextMessageStartEvent {
445    fn default() -> Self {
446        Self::new()
447    }
448}
449
450/// Event containing a piece of thinking text message content.
451///
452/// This event contains chunks of the agent's internal thinking process.
453/// Unlike regular content events, thinking content doesn't validate for
454/// empty delta as it may represent the start of a stream.
455#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
456pub struct ThinkingTextMessageContentEvent {
457    /// Common event fields (timestamp, rawEvent).
458    #[serde(flatten)]
459    pub base: BaseEvent,
460    /// The thinking content delta.
461    pub delta: String,
462}
463
464impl ThinkingTextMessageContentEvent {
465    /// Creates a new ThinkingTextMessageContentEvent.
466    pub fn new(delta: impl Into<String>) -> Self {
467        Self {
468            base: BaseEvent::default(),
469            delta: delta.into(),
470        }
471    }
472
473    /// Sets the timestamp for this event.
474    pub fn with_timestamp(mut self, timestamp: f64) -> Self {
475        self.base.timestamp = Some(timestamp);
476        self
477    }
478}
479
480/// Event indicating the end of a thinking text message.
481///
482/// This event is sent when the agent completes its internal thinking process.
483#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
484pub struct ThinkingTextMessageEndEvent {
485    /// Common event fields (timestamp, rawEvent).
486    #[serde(flatten)]
487    pub base: BaseEvent,
488}
489
490impl ThinkingTextMessageEndEvent {
491    /// Creates a new ThinkingTextMessageEndEvent.
492    pub fn new() -> Self {
493        Self {
494            base: BaseEvent::default(),
495        }
496    }
497
498    /// Sets the timestamp for this event.
499    pub fn with_timestamp(mut self, timestamp: f64) -> Self {
500        self.base.timestamp = Some(timestamp);
501        self
502    }
503}
504
505impl Default for ThinkingTextMessageEndEvent {
506    fn default() -> Self {
507        Self::new()
508    }
509}
510
511// =============================================================================
512// Tool Call Events
513// =============================================================================
514
515/// Event indicating the start of a tool call.
516///
517/// This event is sent when the agent begins calling a tool with specific parameters.
518/// The tool_call_id identifies this call throughout the streaming process.
519#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
520pub struct ToolCallStartEvent {
521    /// Common event fields (timestamp, rawEvent).
522    #[serde(flatten)]
523    pub base: BaseEvent,
524    /// Unique identifier for this tool call.
525    #[serde(rename = "toolCallId")]
526    pub tool_call_id: ToolCallId,
527    /// Name of the tool being called.
528    #[serde(rename = "toolCallName")]
529    pub tool_call_name: String,
530    /// Optional parent message ID if this call is part of a message.
531    #[serde(rename = "parentMessageId", skip_serializing_if = "Option::is_none")]
532    pub parent_message_id: Option<MessageId>,
533}
534
535impl ToolCallStartEvent {
536    /// Creates a new ToolCallStartEvent.
537    pub fn new(tool_call_id: impl Into<ToolCallId>, tool_call_name: impl Into<String>) -> Self {
538        Self {
539            base: BaseEvent::default(),
540            tool_call_id: tool_call_id.into(),
541            tool_call_name: tool_call_name.into(),
542            parent_message_id: None,
543        }
544    }
545
546    /// Sets the parent message ID.
547    pub fn with_parent_message_id(mut self, message_id: impl Into<MessageId>) -> Self {
548        self.parent_message_id = Some(message_id.into());
549        self
550    }
551
552    /// Sets the timestamp for this event.
553    pub fn with_timestamp(mut self, timestamp: f64) -> Self {
554        self.base.timestamp = Some(timestamp);
555        self
556    }
557}
558
559/// Event containing tool call arguments.
560///
561/// This event contains chunks of the arguments being passed to a tool.
562/// Arguments are streamed as JSON string deltas.
563#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
564pub struct ToolCallArgsEvent {
565    /// Common event fields (timestamp, rawEvent).
566    #[serde(flatten)]
567    pub base: BaseEvent,
568    /// The tool call ID this argument chunk belongs to.
569    #[serde(rename = "toolCallId")]
570    pub tool_call_id: ToolCallId,
571    /// The argument delta (JSON string chunk).
572    pub delta: String,
573}
574
575impl ToolCallArgsEvent {
576    /// Creates a new ToolCallArgsEvent.
577    pub fn new(tool_call_id: impl Into<ToolCallId>, delta: impl Into<String>) -> Self {
578        Self {
579            base: BaseEvent::default(),
580            tool_call_id: tool_call_id.into(),
581            delta: delta.into(),
582        }
583    }
584
585    /// Sets the timestamp for this event.
586    pub fn with_timestamp(mut self, timestamp: f64) -> Self {
587        self.base.timestamp = Some(timestamp);
588        self
589    }
590}
591
592/// Event indicating the end of a tool call.
593///
594/// This event is sent when the agent completes sending arguments to a tool.
595#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
596pub struct ToolCallEndEvent {
597    /// Common event fields (timestamp, rawEvent).
598    #[serde(flatten)]
599    pub base: BaseEvent,
600    /// The tool call ID that has completed.
601    #[serde(rename = "toolCallId")]
602    pub tool_call_id: ToolCallId,
603}
604
605impl ToolCallEndEvent {
606    /// Creates a new ToolCallEndEvent.
607    pub fn new(tool_call_id: impl Into<ToolCallId>) -> Self {
608        Self {
609            base: BaseEvent::default(),
610            tool_call_id: tool_call_id.into(),
611        }
612    }
613
614    /// Sets the timestamp for this event.
615    pub fn with_timestamp(mut self, timestamp: f64) -> Self {
616        self.base.timestamp = Some(timestamp);
617        self
618    }
619}
620
621/// Event containing a chunk of tool call content.
622///
623/// This event combines start, args, and potentially end information in a single event.
624/// Used as a non-streaming alternative where all fields may be optional.
625#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
626pub struct ToolCallChunkEvent {
627    /// Common event fields (timestamp, rawEvent).
628    #[serde(flatten)]
629    pub base: BaseEvent,
630    /// Optional tool call ID (may be omitted for continuation chunks).
631    #[serde(rename = "toolCallId", skip_serializing_if = "Option::is_none")]
632    pub tool_call_id: Option<ToolCallId>,
633    /// Optional tool name.
634    #[serde(rename = "toolCallName", skip_serializing_if = "Option::is_none")]
635    pub tool_call_name: Option<String>,
636    /// Optional parent message ID.
637    #[serde(rename = "parentMessageId", skip_serializing_if = "Option::is_none")]
638    pub parent_message_id: Option<MessageId>,
639    /// Optional argument delta.
640    #[serde(skip_serializing_if = "Option::is_none")]
641    pub delta: Option<String>,
642}
643
644impl ToolCallChunkEvent {
645    /// Creates a new empty ToolCallChunkEvent.
646    pub fn new() -> Self {
647        Self {
648            base: BaseEvent::default(),
649            tool_call_id: None,
650            tool_call_name: None,
651            parent_message_id: None,
652            delta: None,
653        }
654    }
655
656    /// Sets the tool call ID.
657    pub fn with_tool_call_id(mut self, tool_call_id: impl Into<ToolCallId>) -> Self {
658        self.tool_call_id = Some(tool_call_id.into());
659        self
660    }
661
662    /// Sets the tool call name.
663    pub fn with_tool_call_name(mut self, name: impl Into<String>) -> Self {
664        self.tool_call_name = Some(name.into());
665        self
666    }
667
668    /// Sets the parent message ID.
669    pub fn with_parent_message_id(mut self, message_id: impl Into<MessageId>) -> Self {
670        self.parent_message_id = Some(message_id.into());
671        self
672    }
673
674    /// Sets the delta.
675    pub fn with_delta(mut self, delta: impl Into<String>) -> Self {
676        self.delta = Some(delta.into());
677        self
678    }
679
680    /// Sets the timestamp for this event.
681    pub fn with_timestamp(mut self, timestamp: f64) -> Self {
682        self.base.timestamp = Some(timestamp);
683        self
684    }
685}
686
687impl Default for ToolCallChunkEvent {
688    fn default() -> Self {
689        Self::new()
690    }
691}
692
693/// Event containing the result of a tool call.
694///
695/// This event is sent when a tool has completed execution and returns its result.
696#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
697pub struct ToolCallResultEvent {
698    /// Common event fields (timestamp, rawEvent).
699    #[serde(flatten)]
700    pub base: BaseEvent,
701    /// Message ID for the result message.
702    #[serde(rename = "messageId")]
703    pub message_id: MessageId,
704    /// The tool call ID this result corresponds to.
705    #[serde(rename = "toolCallId")]
706    pub tool_call_id: ToolCallId,
707    /// The result content.
708    pub content: String,
709    /// Role (always Tool).
710    #[serde(default = "Role::tool")]
711    pub role: Role,
712}
713
714impl ToolCallResultEvent {
715    /// Creates a new ToolCallResultEvent.
716    pub fn new(
717        message_id: impl Into<MessageId>,
718        tool_call_id: impl Into<ToolCallId>,
719        content: impl Into<String>,
720    ) -> Self {
721        Self {
722            base: BaseEvent::default(),
723            message_id: message_id.into(),
724            tool_call_id: tool_call_id.into(),
725            content: content.into(),
726            role: Role::Tool,
727        }
728    }
729
730    /// Sets the timestamp for this event.
731    pub fn with_timestamp(mut self, timestamp: f64) -> Self {
732        self.base.timestamp = Some(timestamp);
733        self
734    }
735}
736
737// =============================================================================
738// Run Lifecycle Events
739// =============================================================================
740
741/// Event indicating that a run has started.
742///
743/// This event is sent when an agent run begins execution within a specific thread.
744/// A run represents a single agent execution that may produce multiple events.
745#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
746pub struct RunStartedEvent {
747    /// Common event fields (timestamp, rawEvent).
748    #[serde(flatten)]
749    pub base: BaseEvent,
750    /// The thread ID this run belongs to.
751    #[serde(rename = "threadId")]
752    pub thread_id: ThreadId,
753    /// Unique identifier for this run.
754    #[serde(rename = "runId")]
755    pub run_id: RunId,
756}
757
758impl RunStartedEvent {
759    /// Creates a new RunStartedEvent.
760    pub fn new(thread_id: impl Into<ThreadId>, run_id: impl Into<RunId>) -> Self {
761        Self {
762            base: BaseEvent::default(),
763            thread_id: thread_id.into(),
764            run_id: run_id.into(),
765        }
766    }
767
768    /// Sets the timestamp for this event.
769    pub fn with_timestamp(mut self, timestamp: f64) -> Self {
770        self.base.timestamp = Some(timestamp);
771        self
772    }
773}
774
775/// Outcome of a run finishing.
776///
777/// Used to indicate whether a run completed successfully or was interrupted
778/// for human-in-the-loop interaction.
779#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
780#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
781pub enum RunFinishedOutcome {
782    /// Run completed successfully.
783    Success,
784    /// Run was interrupted and requires human input to continue.
785    Interrupt,
786}
787
788impl Default for RunFinishedOutcome {
789    fn default() -> Self {
790        Self::Success
791    }
792}
793
794/// Information about a run interrupt.
795///
796/// When a run finishes with `outcome == Interrupt`, this struct contains
797/// information about why the interrupt occurred and what input is needed.
798///
799/// # Example
800///
801/// ```rust
802/// use ag_ui_core::InterruptInfo;
803///
804/// let info = InterruptInfo::new()
805///     .with_id("approval-001")
806///     .with_reason("human_approval")
807///     .with_payload(serde_json::json!({
808///         "action": "DELETE",
809///         "table": "users",
810///         "affectedRows": 42
811///     }));
812/// ```
813#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
814pub struct InterruptInfo {
815    /// Optional identifier for tracking this interrupt across resume.
816    #[serde(skip_serializing_if = "Option::is_none")]
817    pub id: Option<String>,
818    /// Optional reason describing why the interrupt occurred.
819    /// Common values: "human_approval", "upload_required", "policy_hold"
820    #[serde(skip_serializing_if = "Option::is_none")]
821    pub reason: Option<String>,
822    /// Optional payload with context for the interrupt UI.
823    /// Contains arbitrary JSON data for rendering approval forms, proposals, etc.
824    #[serde(skip_serializing_if = "Option::is_none")]
825    pub payload: Option<JsonValue>,
826}
827
828impl InterruptInfo {
829    /// Creates a new empty InterruptInfo.
830    pub fn new() -> Self {
831        Self::default()
832    }
833
834    /// Sets the interrupt ID.
835    pub fn with_id(mut self, id: impl Into<String>) -> Self {
836        self.id = Some(id.into());
837        self
838    }
839
840    /// Sets the interrupt reason.
841    pub fn with_reason(mut self, reason: impl Into<String>) -> Self {
842        self.reason = Some(reason.into());
843        self
844    }
845
846    /// Sets the interrupt payload.
847    pub fn with_payload(mut self, payload: JsonValue) -> Self {
848        self.payload = Some(payload);
849        self
850    }
851}
852
853/// Event indicating that a run has finished.
854///
855/// This event is sent when an agent run completes, either successfully or
856/// with an interrupt requiring human input.
857///
858/// # Interrupt Flow
859///
860/// When `outcome == Interrupt`, the agent indicates that on the next run,
861/// a value needs to be provided via `RunAgentInput.resume` to continue.
862///
863/// # Example
864///
865/// ```rust
866/// use ag_ui_core::{RunFinishedEvent, RunFinishedOutcome, InterruptInfo, ThreadId, RunId};
867///
868/// // Success case
869/// let success = RunFinishedEvent::new(ThreadId::random(), RunId::random())
870///     .with_result(serde_json::json!({"status": "done"}));
871///
872/// // Interrupt case
873/// let interrupt = RunFinishedEvent::new(ThreadId::random(), RunId::random())
874///     .with_interrupt(
875///         InterruptInfo::new()
876///             .with_reason("human_approval")
877///             .with_payload(serde_json::json!({"action": "send_email"}))
878///     );
879/// ```
880#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
881pub struct RunFinishedEvent {
882    /// Common event fields (timestamp, rawEvent).
883    #[serde(flatten)]
884    pub base: BaseEvent,
885    /// The thread ID this run belongs to.
886    #[serde(rename = "threadId")]
887    pub thread_id: ThreadId,
888    /// The run ID that finished.
889    #[serde(rename = "runId")]
890    pub run_id: RunId,
891    /// Outcome of the run. Optional for backward compatibility.
892    /// When omitted, outcome is inferred: if interrupt is present, it's Interrupt; otherwise Success.
893    #[serde(skip_serializing_if = "Option::is_none")]
894    pub outcome: Option<RunFinishedOutcome>,
895    /// Optional result value from the run.
896    /// Present when outcome is Success (or omitted with no interrupt).
897    #[serde(skip_serializing_if = "Option::is_none")]
898    pub result: Option<JsonValue>,
899    /// Optional interrupt information.
900    /// Present when outcome is Interrupt (or omitted with interrupt present).
901    #[serde(skip_serializing_if = "Option::is_none")]
902    pub interrupt: Option<InterruptInfo>,
903}
904
905impl RunFinishedEvent {
906    /// Creates a new RunFinishedEvent with Success outcome.
907    pub fn new(thread_id: impl Into<ThreadId>, run_id: impl Into<RunId>) -> Self {
908        Self {
909            base: BaseEvent::default(),
910            thread_id: thread_id.into(),
911            run_id: run_id.into(),
912            outcome: None,
913            result: None,
914            interrupt: None,
915        }
916    }
917
918    /// Sets the outcome explicitly.
919    pub fn with_outcome(mut self, outcome: RunFinishedOutcome) -> Self {
920        self.outcome = Some(outcome);
921        self
922    }
923
924    /// Sets the result for this event (implies Success outcome).
925    pub fn with_result(mut self, result: JsonValue) -> Self {
926        self.result = Some(result);
927        self
928    }
929
930    /// Sets the interrupt info (implies Interrupt outcome).
931    pub fn with_interrupt(mut self, interrupt: InterruptInfo) -> Self {
932        self.outcome = Some(RunFinishedOutcome::Interrupt);
933        self.interrupt = Some(interrupt);
934        self
935    }
936
937    /// Sets the timestamp for this event.
938    pub fn with_timestamp(mut self, timestamp: f64) -> Self {
939        self.base.timestamp = Some(timestamp);
940        self
941    }
942
943    /// Returns the effective outcome of this event.
944    ///
945    /// If outcome is explicitly set, returns that. Otherwise:
946    /// - If interrupt is present, returns Interrupt
947    /// - Otherwise, returns Success
948    pub fn effective_outcome(&self) -> RunFinishedOutcome {
949        self.outcome.unwrap_or_else(|| {
950            if self.interrupt.is_some() {
951                RunFinishedOutcome::Interrupt
952            } else {
953                RunFinishedOutcome::Success
954            }
955        })
956    }
957}
958
959/// Event indicating that a run has encountered an error.
960///
961/// This event is sent when an agent run fails with an error.
962#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
963pub struct RunErrorEvent {
964    /// Common event fields (timestamp, rawEvent).
965    #[serde(flatten)]
966    pub base: BaseEvent,
967    /// Error message describing what went wrong.
968    pub message: String,
969    /// Optional error code for programmatic handling.
970    #[serde(skip_serializing_if = "Option::is_none")]
971    pub code: Option<String>,
972}
973
974impl RunErrorEvent {
975    /// Creates a new RunErrorEvent.
976    pub fn new(message: impl Into<String>) -> Self {
977        Self {
978            base: BaseEvent::default(),
979            message: message.into(),
980            code: None,
981        }
982    }
983
984    /// Sets the error code.
985    pub fn with_code(mut self, code: impl Into<String>) -> Self {
986        self.code = Some(code.into());
987        self
988    }
989
990    /// Sets the timestamp for this event.
991    pub fn with_timestamp(mut self, timestamp: f64) -> Self {
992        self.base.timestamp = Some(timestamp);
993        self
994    }
995}
996
997// =============================================================================
998// Step Events
999// =============================================================================
1000
1001/// Event indicating that a step has started.
1002///
1003/// This event is sent when a specific named step within a run begins execution.
1004/// Steps allow tracking progress through multi-stage agent workflows.
1005#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1006pub struct StepStartedEvent {
1007    /// Common event fields (timestamp, rawEvent).
1008    #[serde(flatten)]
1009    pub base: BaseEvent,
1010    /// Name of the step that started.
1011    #[serde(rename = "stepName")]
1012    pub step_name: String,
1013}
1014
1015impl StepStartedEvent {
1016    /// Creates a new StepStartedEvent.
1017    pub fn new(step_name: impl Into<String>) -> Self {
1018        Self {
1019            base: BaseEvent::default(),
1020            step_name: step_name.into(),
1021        }
1022    }
1023
1024    /// Sets the timestamp for this event.
1025    pub fn with_timestamp(mut self, timestamp: f64) -> Self {
1026        self.base.timestamp = Some(timestamp);
1027        self
1028    }
1029}
1030
1031/// Event indicating that a step has finished.
1032///
1033/// This event is sent when a specific named step within a run completes execution.
1034#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1035pub struct StepFinishedEvent {
1036    /// Common event fields (timestamp, rawEvent).
1037    #[serde(flatten)]
1038    pub base: BaseEvent,
1039    /// Name of the step that finished.
1040    #[serde(rename = "stepName")]
1041    pub step_name: String,
1042}
1043
1044impl StepFinishedEvent {
1045    /// Creates a new StepFinishedEvent.
1046    pub fn new(step_name: impl Into<String>) -> Self {
1047        Self {
1048            base: BaseEvent::default(),
1049            step_name: step_name.into(),
1050        }
1051    }
1052
1053    /// Sets the timestamp for this event.
1054    pub fn with_timestamp(mut self, timestamp: f64) -> Self {
1055        self.base.timestamp = Some(timestamp);
1056        self
1057    }
1058}
1059
1060// =============================================================================
1061// State Events
1062// =============================================================================
1063
1064/// Event containing a complete state snapshot.
1065///
1066/// This event is sent to provide the full current state of the agent.
1067/// The state is generic over `StateT` which must implement `AgentState`.
1068///
1069/// # Type Parameter
1070///
1071/// - `StateT`: The type of state, defaults to `JsonValue` for flexibility.
1072#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1073#[serde(bound(deserialize = ""))]
1074pub struct StateSnapshotEvent<StateT: AgentState = JsonValue> {
1075    /// Common event fields (timestamp, rawEvent).
1076    #[serde(flatten)]
1077    pub base: BaseEvent,
1078    /// The complete state snapshot.
1079    pub snapshot: StateT,
1080}
1081
1082impl<StateT: AgentState> StateSnapshotEvent<StateT> {
1083    /// Creates a new StateSnapshotEvent with the given state.
1084    pub fn new(snapshot: StateT) -> Self {
1085        Self {
1086            base: BaseEvent::default(),
1087            snapshot,
1088        }
1089    }
1090
1091    /// Sets the timestamp for this event.
1092    pub fn with_timestamp(mut self, timestamp: f64) -> Self {
1093        self.base.timestamp = Some(timestamp);
1094        self
1095    }
1096}
1097
1098impl<StateT: AgentState + Default> Default for StateSnapshotEvent<StateT> {
1099    fn default() -> Self {
1100        Self {
1101            base: BaseEvent::default(),
1102            snapshot: StateT::default(),
1103        }
1104    }
1105}
1106
1107/// Event containing incremental state updates as JSON Patch operations.
1108///
1109/// This event is sent to update state incrementally using RFC 6902 JSON Patch format.
1110/// The delta is a vector of patch operations (add, remove, replace, move, copy, test).
1111#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1112pub struct StateDeltaEvent {
1113    /// Common event fields (timestamp, rawEvent).
1114    #[serde(flatten)]
1115    pub base: BaseEvent,
1116    /// JSON Patch operations per RFC 6902.
1117    pub delta: Vec<JsonValue>,
1118}
1119
1120impl StateDeltaEvent {
1121    /// Creates a new StateDeltaEvent with the given patch operations.
1122    pub fn new(delta: Vec<JsonValue>) -> Self {
1123        Self {
1124            base: BaseEvent::default(),
1125            delta,
1126        }
1127    }
1128
1129    /// Sets the timestamp for this event.
1130    pub fn with_timestamp(mut self, timestamp: f64) -> Self {
1131        self.base.timestamp = Some(timestamp);
1132        self
1133    }
1134}
1135
1136impl Default for StateDeltaEvent {
1137    fn default() -> Self {
1138        Self {
1139            base: BaseEvent::default(),
1140            delta: Vec::new(),
1141        }
1142    }
1143}
1144
1145/// Event containing a complete snapshot of all messages.
1146///
1147/// This event is sent to provide the full message history to the client.
1148#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1149pub struct MessagesSnapshotEvent {
1150    /// Common event fields (timestamp, rawEvent).
1151    #[serde(flatten)]
1152    pub base: BaseEvent,
1153    /// Complete list of messages.
1154    pub messages: Vec<Message>,
1155}
1156
1157impl MessagesSnapshotEvent {
1158    /// Creates a new MessagesSnapshotEvent with the given messages.
1159    pub fn new(messages: Vec<Message>) -> Self {
1160        Self {
1161            base: BaseEvent::default(),
1162            messages,
1163        }
1164    }
1165
1166    /// Sets the timestamp for this event.
1167    pub fn with_timestamp(mut self, timestamp: f64) -> Self {
1168        self.base.timestamp = Some(timestamp);
1169        self
1170    }
1171}
1172
1173impl Default for MessagesSnapshotEvent {
1174    fn default() -> Self {
1175        Self {
1176            base: BaseEvent::default(),
1177            messages: Vec::new(),
1178        }
1179    }
1180}
1181
1182// =============================================================================
1183// Activity Events
1184// =============================================================================
1185
1186/// Event containing a complete activity snapshot.
1187///
1188/// This event creates a new activity message or replaces an existing one.
1189/// Activity messages track structured agent activities like planning or research.
1190///
1191/// # Example
1192///
1193/// ```rust
1194/// use ag_ui_core::event::ActivitySnapshotEvent;
1195/// use ag_ui_core::MessageId;
1196/// use serde_json::json;
1197///
1198/// let event = ActivitySnapshotEvent::new(
1199///     MessageId::random(),
1200///     "PLAN",
1201///     json!({"steps": ["research", "implement", "test"]}),
1202/// );
1203/// ```
1204#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1205pub struct ActivitySnapshotEvent {
1206    /// Common event fields (timestamp, rawEvent).
1207    #[serde(flatten)]
1208    pub base: BaseEvent,
1209    /// The message ID for this activity.
1210    #[serde(rename = "messageId")]
1211    pub message_id: MessageId,
1212    /// The type of activity (e.g., "PLAN", "RESEARCH").
1213    #[serde(rename = "activityType")]
1214    pub activity_type: String,
1215    /// The activity content as a flexible JSON object.
1216    pub content: JsonValue,
1217    /// Whether to replace the existing activity content (default: true).
1218    #[serde(skip_serializing_if = "Option::is_none")]
1219    pub replace: Option<bool>,
1220}
1221
1222impl ActivitySnapshotEvent {
1223    /// Creates a new ActivitySnapshotEvent with the given message ID, type, and content.
1224    pub fn new(
1225        message_id: impl Into<MessageId>,
1226        activity_type: impl Into<String>,
1227        content: JsonValue,
1228    ) -> Self {
1229        Self {
1230            base: BaseEvent::default(),
1231            message_id: message_id.into(),
1232            activity_type: activity_type.into(),
1233            content,
1234            replace: None,
1235        }
1236    }
1237
1238    /// Sets whether to replace the existing activity content.
1239    pub fn with_replace(mut self, replace: bool) -> Self {
1240        self.replace = Some(replace);
1241        self
1242    }
1243
1244    /// Sets the timestamp for this event.
1245    pub fn with_timestamp(mut self, timestamp: f64) -> Self {
1246        self.base.timestamp = Some(timestamp);
1247        self
1248    }
1249}
1250
1251/// Event containing an incremental activity update.
1252///
1253/// This event applies a JSON Patch (RFC 6902) to an existing activity's content.
1254/// Use this for efficient partial updates to activity content.
1255///
1256/// # Example
1257///
1258/// ```rust
1259/// use ag_ui_core::event::ActivityDeltaEvent;
1260/// use ag_ui_core::MessageId;
1261/// use serde_json::json;
1262///
1263/// let event = ActivityDeltaEvent::new(
1264///     MessageId::random(),
1265///     "PLAN",
1266///     vec![json!({"op": "add", "path": "/steps/-", "value": "deploy"})],
1267/// );
1268/// ```
1269#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1270pub struct ActivityDeltaEvent {
1271    /// Common event fields (timestamp, rawEvent).
1272    #[serde(flatten)]
1273    pub base: BaseEvent,
1274    /// The message ID for this activity.
1275    #[serde(rename = "messageId")]
1276    pub message_id: MessageId,
1277    /// The type of activity (e.g., "PLAN", "RESEARCH").
1278    #[serde(rename = "activityType")]
1279    pub activity_type: String,
1280    /// JSON Patch operations (RFC 6902) to apply to the content.
1281    pub patch: Vec<JsonValue>,
1282}
1283
1284impl ActivityDeltaEvent {
1285    /// Creates a new ActivityDeltaEvent with the given message ID, type, and patch.
1286    pub fn new(
1287        message_id: impl Into<MessageId>,
1288        activity_type: impl Into<String>,
1289        patch: Vec<JsonValue>,
1290    ) -> Self {
1291        Self {
1292            base: BaseEvent::default(),
1293            message_id: message_id.into(),
1294            activity_type: activity_type.into(),
1295            patch,
1296        }
1297    }
1298
1299    /// Sets the timestamp for this event.
1300    pub fn with_timestamp(mut self, timestamp: f64) -> Self {
1301        self.base.timestamp = Some(timestamp);
1302        self
1303    }
1304}
1305
1306// =============================================================================
1307// Thinking Step Events
1308// =============================================================================
1309
1310/// Event indicating that a thinking step has started.
1311///
1312/// This event is sent when the agent begins a chain-of-thought reasoning step.
1313/// Unlike ThinkingTextMessage events (which contain actual thinking content),
1314/// this event marks the boundary of a thinking block.
1315#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1316pub struct ThinkingStartEvent {
1317    /// Common event fields (timestamp, rawEvent).
1318    #[serde(flatten)]
1319    pub base: BaseEvent,
1320    /// Optional title for the thinking step.
1321    #[serde(skip_serializing_if = "Option::is_none")]
1322    pub title: Option<String>,
1323}
1324
1325impl ThinkingStartEvent {
1326    /// Creates a new ThinkingStartEvent.
1327    pub fn new() -> Self {
1328        Self {
1329            base: BaseEvent::default(),
1330            title: None,
1331        }
1332    }
1333
1334    /// Sets the title for this thinking step.
1335    pub fn with_title(mut self, title: impl Into<String>) -> Self {
1336        self.title = Some(title.into());
1337        self
1338    }
1339
1340    /// Sets the timestamp for this event.
1341    pub fn with_timestamp(mut self, timestamp: f64) -> Self {
1342        self.base.timestamp = Some(timestamp);
1343        self
1344    }
1345}
1346
1347impl Default for ThinkingStartEvent {
1348    fn default() -> Self {
1349        Self::new()
1350    }
1351}
1352
1353/// Event indicating that a thinking step has ended.
1354///
1355/// This event is sent when the agent completes a chain-of-thought reasoning step.
1356#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1357pub struct ThinkingEndEvent {
1358    /// Common event fields (timestamp, rawEvent).
1359    #[serde(flatten)]
1360    pub base: BaseEvent,
1361}
1362
1363impl ThinkingEndEvent {
1364    /// Creates a new ThinkingEndEvent.
1365    pub fn new() -> Self {
1366        Self {
1367            base: BaseEvent::default(),
1368        }
1369    }
1370
1371    /// Sets the timestamp for this event.
1372    pub fn with_timestamp(mut self, timestamp: f64) -> Self {
1373        self.base.timestamp = Some(timestamp);
1374        self
1375    }
1376}
1377
1378impl Default for ThinkingEndEvent {
1379    fn default() -> Self {
1380        Self::new()
1381    }
1382}
1383
1384// =============================================================================
1385// Special Events
1386// =============================================================================
1387
1388/// Event containing raw data from the underlying provider.
1389///
1390/// This event is sent to pass through raw provider-specific data that
1391/// doesn't fit into other event types.
1392#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1393pub struct RawEvent {
1394    /// Common event fields (timestamp, rawEvent).
1395    #[serde(flatten)]
1396    pub base: BaseEvent,
1397    /// The raw event data.
1398    pub event: JsonValue,
1399    /// Optional source identifier for the raw event.
1400    #[serde(skip_serializing_if = "Option::is_none")]
1401    pub source: Option<String>,
1402}
1403
1404impl RawEvent {
1405    /// Creates a new RawEvent with the given event data.
1406    pub fn new(event: JsonValue) -> Self {
1407        Self {
1408            base: BaseEvent::default(),
1409            event,
1410            source: None,
1411        }
1412    }
1413
1414    /// Sets the source identifier.
1415    pub fn with_source(mut self, source: impl Into<String>) -> Self {
1416        self.source = Some(source.into());
1417        self
1418    }
1419
1420    /// Sets the timestamp for this event.
1421    pub fn with_timestamp(mut self, timestamp: f64) -> Self {
1422        self.base.timestamp = Some(timestamp);
1423        self
1424    }
1425}
1426
1427/// Event for custom application-specific data.
1428///
1429/// This event allows sending arbitrary named events with custom payloads.
1430#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1431pub struct CustomEvent {
1432    /// Common event fields (timestamp, rawEvent).
1433    #[serde(flatten)]
1434    pub base: BaseEvent,
1435    /// Name of the custom event.
1436    pub name: String,
1437    /// Custom event payload.
1438    pub value: JsonValue,
1439}
1440
1441impl CustomEvent {
1442    /// Creates a new CustomEvent with the given name and value.
1443    pub fn new(name: impl Into<String>, value: JsonValue) -> Self {
1444        Self {
1445            base: BaseEvent::default(),
1446            name: name.into(),
1447            value,
1448        }
1449    }
1450
1451    /// Sets the timestamp for this event.
1452    pub fn with_timestamp(mut self, timestamp: f64) -> Self {
1453        self.base.timestamp = Some(timestamp);
1454        self
1455    }
1456}
1457
1458// =============================================================================
1459// Event Union
1460// =============================================================================
1461
1462/// Union of all possible events in the Agent User Interaction Protocol.
1463///
1464/// This enum represents any event that can be sent or received in the AG-UI protocol.
1465/// Events are serialized with a `type` discriminant in SCREAMING_SNAKE_CASE format.
1466///
1467/// # Type Parameter
1468///
1469/// - `StateT`: The type of state for `StateSnapshot` events, defaults to `JsonValue`.
1470///
1471/// # Serialization
1472///
1473/// Events are serialized as JSON objects with a `type` field indicating the variant:
1474/// ```json
1475/// {"type": "TEXT_MESSAGE_START", "messageId": "...", "role": "assistant"}
1476/// ```
1477#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1478#[serde(tag = "type", rename_all = "SCREAMING_SNAKE_CASE", bound(deserialize = ""))]
1479pub enum Event<StateT: AgentState = JsonValue> {
1480    /// Start of a text message from the assistant.
1481    TextMessageStart(TextMessageStartEvent),
1482    /// Content chunk of a text message (streaming delta).
1483    TextMessageContent(TextMessageContentEvent),
1484    /// End of a text message.
1485    TextMessageEnd(TextMessageEndEvent),
1486    /// Complete text message chunk (non-streaming alternative).
1487    TextMessageChunk(TextMessageChunkEvent),
1488    /// Start of a thinking text message (extended thinking).
1489    ThinkingTextMessageStart(ThinkingTextMessageStartEvent),
1490    /// Content chunk of a thinking text message.
1491    ThinkingTextMessageContent(ThinkingTextMessageContentEvent),
1492    /// End of a thinking text message.
1493    ThinkingTextMessageEnd(ThinkingTextMessageEndEvent),
1494    /// Start of a tool call.
1495    ToolCallStart(ToolCallStartEvent),
1496    /// Arguments chunk for a tool call (streaming).
1497    ToolCallArgs(ToolCallArgsEvent),
1498    /// End of a tool call.
1499    ToolCallEnd(ToolCallEndEvent),
1500    /// Complete tool call chunk (non-streaming alternative).
1501    ToolCallChunk(ToolCallChunkEvent),
1502    /// Result of a tool call execution.
1503    ToolCallResult(ToolCallResultEvent),
1504    /// Start of a thinking step (chain-of-thought boundary).
1505    ThinkingStart(ThinkingStartEvent),
1506    /// End of a thinking step.
1507    ThinkingEnd(ThinkingEndEvent),
1508    /// Complete state snapshot.
1509    StateSnapshot(StateSnapshotEvent<StateT>),
1510    /// Incremental state update (JSON Patch).
1511    StateDelta(StateDeltaEvent),
1512    /// Complete messages snapshot.
1513    MessagesSnapshot(MessagesSnapshotEvent),
1514    /// Complete activity snapshot.
1515    ActivitySnapshot(ActivitySnapshotEvent),
1516    /// Incremental activity update (JSON Patch).
1517    ActivityDelta(ActivityDeltaEvent),
1518    /// Raw event from the underlying provider.
1519    Raw(RawEvent),
1520    /// Custom application-specific event.
1521    Custom(CustomEvent),
1522    /// Agent run has started.
1523    RunStarted(RunStartedEvent),
1524    /// Agent run has finished successfully.
1525    RunFinished(RunFinishedEvent),
1526    /// Agent run encountered an error.
1527    RunError(RunErrorEvent),
1528    /// A step within a run has started.
1529    StepStarted(StepStartedEvent),
1530    /// A step within a run has finished.
1531    StepFinished(StepFinishedEvent),
1532}
1533
1534impl<StateT: AgentState> Event<StateT> {
1535    /// Returns the event type for this event.
1536    pub fn event_type(&self) -> EventType {
1537        match self {
1538            Event::TextMessageStart(_) => EventType::TextMessageStart,
1539            Event::TextMessageContent(_) => EventType::TextMessageContent,
1540            Event::TextMessageEnd(_) => EventType::TextMessageEnd,
1541            Event::TextMessageChunk(_) => EventType::TextMessageChunk,
1542            Event::ThinkingTextMessageStart(_) => EventType::ThinkingTextMessageStart,
1543            Event::ThinkingTextMessageContent(_) => EventType::ThinkingTextMessageContent,
1544            Event::ThinkingTextMessageEnd(_) => EventType::ThinkingTextMessageEnd,
1545            Event::ToolCallStart(_) => EventType::ToolCallStart,
1546            Event::ToolCallArgs(_) => EventType::ToolCallArgs,
1547            Event::ToolCallEnd(_) => EventType::ToolCallEnd,
1548            Event::ToolCallChunk(_) => EventType::ToolCallChunk,
1549            Event::ToolCallResult(_) => EventType::ToolCallResult,
1550            Event::ThinkingStart(_) => EventType::ThinkingStart,
1551            Event::ThinkingEnd(_) => EventType::ThinkingEnd,
1552            Event::StateSnapshot(_) => EventType::StateSnapshot,
1553            Event::StateDelta(_) => EventType::StateDelta,
1554            Event::MessagesSnapshot(_) => EventType::MessagesSnapshot,
1555            Event::ActivitySnapshot(_) => EventType::ActivitySnapshot,
1556            Event::ActivityDelta(_) => EventType::ActivityDelta,
1557            Event::Raw(_) => EventType::Raw,
1558            Event::Custom(_) => EventType::Custom,
1559            Event::RunStarted(_) => EventType::RunStarted,
1560            Event::RunFinished(_) => EventType::RunFinished,
1561            Event::RunError(_) => EventType::RunError,
1562            Event::StepStarted(_) => EventType::StepStarted,
1563            Event::StepFinished(_) => EventType::StepFinished,
1564        }
1565    }
1566
1567    /// Returns the timestamp of this event if available.
1568    pub fn timestamp(&self) -> Option<f64> {
1569        match self {
1570            Event::TextMessageStart(e) => e.base.timestamp,
1571            Event::TextMessageContent(e) => e.base.timestamp,
1572            Event::TextMessageEnd(e) => e.base.timestamp,
1573            Event::TextMessageChunk(e) => e.base.timestamp,
1574            Event::ThinkingTextMessageStart(e) => e.base.timestamp,
1575            Event::ThinkingTextMessageContent(e) => e.base.timestamp,
1576            Event::ThinkingTextMessageEnd(e) => e.base.timestamp,
1577            Event::ToolCallStart(e) => e.base.timestamp,
1578            Event::ToolCallArgs(e) => e.base.timestamp,
1579            Event::ToolCallEnd(e) => e.base.timestamp,
1580            Event::ToolCallChunk(e) => e.base.timestamp,
1581            Event::ToolCallResult(e) => e.base.timestamp,
1582            Event::ThinkingStart(e) => e.base.timestamp,
1583            Event::ThinkingEnd(e) => e.base.timestamp,
1584            Event::StateSnapshot(e) => e.base.timestamp,
1585            Event::StateDelta(e) => e.base.timestamp,
1586            Event::MessagesSnapshot(e) => e.base.timestamp,
1587            Event::ActivitySnapshot(e) => e.base.timestamp,
1588            Event::ActivityDelta(e) => e.base.timestamp,
1589            Event::Raw(e) => e.base.timestamp,
1590            Event::Custom(e) => e.base.timestamp,
1591            Event::RunStarted(e) => e.base.timestamp,
1592            Event::RunFinished(e) => e.base.timestamp,
1593            Event::RunError(e) => e.base.timestamp,
1594            Event::StepStarted(e) => e.base.timestamp,
1595            Event::StepFinished(e) => e.base.timestamp,
1596        }
1597    }
1598}
1599
1600#[cfg(test)]
1601mod tests {
1602    use super::*;
1603
1604    #[test]
1605    fn test_event_type_serialization() {
1606        let event_type = EventType::TextMessageStart;
1607        let json = serde_json::to_string(&event_type).unwrap();
1608        assert_eq!(json, "\"TEXT_MESSAGE_START\"");
1609
1610        let event_type = EventType::ToolCallArgs;
1611        let json = serde_json::to_string(&event_type).unwrap();
1612        assert_eq!(json, "\"TOOL_CALL_ARGS\"");
1613
1614        let event_type = EventType::StateSnapshot;
1615        let json = serde_json::to_string(&event_type).unwrap();
1616        assert_eq!(json, "\"STATE_SNAPSHOT\"");
1617    }
1618
1619    #[test]
1620    fn test_event_type_deserialization() {
1621        let event_type: EventType = serde_json::from_str("\"RUN_STARTED\"").unwrap();
1622        assert_eq!(event_type, EventType::RunStarted);
1623
1624        let event_type: EventType = serde_json::from_str("\"THINKING_TEXT_MESSAGE_CONTENT\"").unwrap();
1625        assert_eq!(event_type, EventType::ThinkingTextMessageContent);
1626    }
1627
1628    #[test]
1629    fn test_event_type_as_str() {
1630        assert_eq!(EventType::TextMessageStart.as_str(), "TEXT_MESSAGE_START");
1631        assert_eq!(EventType::RunFinished.as_str(), "RUN_FINISHED");
1632        assert_eq!(EventType::Custom.as_str(), "CUSTOM");
1633    }
1634
1635    #[test]
1636    fn test_event_type_display() {
1637        assert_eq!(format!("{}", EventType::TextMessageStart), "TEXT_MESSAGE_START");
1638        assert_eq!(format!("{}", EventType::StateDelta), "STATE_DELTA");
1639    }
1640
1641    #[test]
1642    fn test_base_event_serialization() {
1643        let event = BaseEvent {
1644            timestamp: Some(1706123456789.0),
1645            raw_event: None,
1646        };
1647        let json = serde_json::to_string(&event).unwrap();
1648        assert!(json.contains("\"timestamp\":1706123456789.0"));
1649        assert!(!json.contains("rawEvent")); // skipped when None
1650    }
1651
1652    #[test]
1653    fn test_base_event_with_raw_event() {
1654        let event = BaseEvent {
1655            timestamp: None,
1656            raw_event: Some(serde_json::json!({"provider": "openai"})),
1657        };
1658        let json = serde_json::to_string(&event).unwrap();
1659        assert!(json.contains("\"rawEvent\""));
1660        assert!(json.contains("\"provider\":\"openai\""));
1661    }
1662
1663    #[test]
1664    fn test_base_event_builder() {
1665        let event = BaseEvent::new()
1666            .timestamp(1234567890.0)
1667            .raw_event(serde_json::json!({"test": true}));
1668
1669        assert_eq!(event.timestamp, Some(1234567890.0));
1670        assert!(event.raw_event.is_some());
1671    }
1672
1673    #[test]
1674    fn test_event_validation_error_display() {
1675        let error = EventValidationError::EmptyDelta;
1676        assert_eq!(error.to_string(), "Delta must not be an empty string");
1677
1678        let error = EventValidationError::InvalidFormat("bad json".to_string());
1679        assert_eq!(error.to_string(), "Invalid event format: bad json");
1680
1681        let error = EventValidationError::MissingField("message_id".to_string());
1682        assert_eq!(error.to_string(), "Missing required field: message_id");
1683
1684        let error = EventValidationError::TypeMismatch {
1685            expected: "TEXT_MESSAGE_START".to_string(),
1686            actual: "RUN_STARTED".to_string(),
1687        };
1688        assert_eq!(
1689            error.to_string(),
1690            "Event type mismatch: expected TEXT_MESSAGE_START, got RUN_STARTED"
1691        );
1692    }
1693
1694    #[test]
1695    fn test_event_validation_error_is_std_error() {
1696        fn requires_error<E: std::error::Error>(_: E) {}
1697        requires_error(EventValidationError::EmptyDelta);
1698    }
1699
1700    #[test]
1701    fn test_all_event_types_roundtrip() {
1702        let all_types = [
1703            EventType::TextMessageStart,
1704            EventType::TextMessageContent,
1705            EventType::TextMessageEnd,
1706            EventType::TextMessageChunk,
1707            EventType::ThinkingTextMessageStart,
1708            EventType::ThinkingTextMessageContent,
1709            EventType::ThinkingTextMessageEnd,
1710            EventType::ToolCallStart,
1711            EventType::ToolCallArgs,
1712            EventType::ToolCallEnd,
1713            EventType::ToolCallChunk,
1714            EventType::ToolCallResult,
1715            EventType::ThinkingStart,
1716            EventType::ThinkingEnd,
1717            EventType::StateSnapshot,
1718            EventType::StateDelta,
1719            EventType::MessagesSnapshot,
1720            EventType::ActivitySnapshot,
1721            EventType::ActivityDelta,
1722            EventType::Raw,
1723            EventType::Custom,
1724            EventType::RunStarted,
1725            EventType::RunFinished,
1726            EventType::RunError,
1727            EventType::StepStarted,
1728            EventType::StepFinished,
1729        ];
1730
1731        for event_type in all_types {
1732            let json = serde_json::to_string(&event_type).unwrap();
1733            let parsed: EventType = serde_json::from_str(&json).unwrap();
1734            assert_eq!(event_type, parsed);
1735        }
1736    }
1737
1738    // =========================================================================
1739    // Text Message Event Tests
1740    // =========================================================================
1741
1742    #[test]
1743    fn test_text_message_start_event() {
1744        use crate::types::{MessageId, Role};
1745
1746        let event = TextMessageStartEvent::new(MessageId::random());
1747        assert_eq!(event.role, Role::Assistant);
1748
1749        let json = serde_json::to_string(&event).unwrap();
1750        assert!(json.contains("\"messageId\""));
1751        assert!(json.contains("\"role\":\"assistant\""));
1752    }
1753
1754    #[test]
1755    fn test_text_message_start_event_with_timestamp() {
1756        use crate::types::MessageId;
1757
1758        let event = TextMessageStartEvent::new(MessageId::random()).with_timestamp(1234567890.0);
1759        assert_eq!(event.base.timestamp, Some(1234567890.0));
1760    }
1761
1762    #[test]
1763    fn test_text_message_content_event_validation() {
1764        use crate::types::MessageId;
1765
1766        // Valid delta
1767        let result = TextMessageContentEvent::new(MessageId::random(), "Hello");
1768        assert!(result.is_ok());
1769
1770        // Empty delta should fail
1771        let result = TextMessageContentEvent::new(MessageId::random(), "");
1772        assert!(matches!(result, Err(EventValidationError::EmptyDelta)));
1773    }
1774
1775    #[test]
1776    fn test_text_message_content_event_validate_method() {
1777        use crate::types::MessageId;
1778
1779        let event = TextMessageContentEvent::new_unchecked(MessageId::random(), "");
1780        assert!(matches!(event.validate(), Err(EventValidationError::EmptyDelta)));
1781
1782        let event = TextMessageContentEvent::new_unchecked(MessageId::random(), "Hello");
1783        assert!(event.validate().is_ok());
1784    }
1785
1786    #[test]
1787    fn test_text_message_content_event_serialization() {
1788        use crate::types::MessageId;
1789
1790        let event = TextMessageContentEvent::new(MessageId::random(), "Hello, world!").unwrap();
1791        let json = serde_json::to_string(&event).unwrap();
1792
1793        assert!(json.contains("\"messageId\""));
1794        assert!(json.contains("\"delta\":\"Hello, world!\""));
1795    }
1796
1797    #[test]
1798    fn test_text_message_end_event() {
1799        use crate::types::MessageId;
1800
1801        let msg_id = MessageId::random();
1802        let event = TextMessageEndEvent::new(msg_id.clone());
1803
1804        let json = serde_json::to_string(&event).unwrap();
1805        assert!(json.contains("\"messageId\""));
1806    }
1807
1808    #[test]
1809    fn test_text_message_chunk_event() {
1810        use crate::types::{MessageId, Role};
1811
1812        let event = TextMessageChunkEvent::new(Role::Assistant)
1813            .with_message_id(MessageId::random())
1814            .with_delta("chunk content");
1815
1816        assert!(event.message_id.is_some());
1817        assert_eq!(event.delta, Some("chunk content".to_string()));
1818
1819        let json = serde_json::to_string(&event).unwrap();
1820        assert!(json.contains("\"messageId\""));
1821        assert!(json.contains("\"delta\":\"chunk content\""));
1822    }
1823
1824    #[test]
1825    fn test_text_message_chunk_event_skips_none() {
1826        use crate::types::Role;
1827
1828        let event = TextMessageChunkEvent::new(Role::Assistant);
1829        let json = serde_json::to_string(&event).unwrap();
1830
1831        // Should not contain optional fields when None
1832        assert!(!json.contains("\"messageId\""));
1833        assert!(!json.contains("\"delta\""));
1834        assert!(json.contains("\"role\":\"assistant\""));
1835    }
1836
1837    // =========================================================================
1838    // Thinking Text Message Event Tests
1839    // =========================================================================
1840
1841    #[test]
1842    fn test_thinking_text_message_start_event() {
1843        let event = ThinkingTextMessageStartEvent::new();
1844        let json = serde_json::to_string(&event).unwrap();
1845
1846        // Should be minimal - just empty object or with timestamp if set
1847        assert_eq!(json, "{}");
1848    }
1849
1850    #[test]
1851    fn test_thinking_text_message_start_event_with_timestamp() {
1852        let event = ThinkingTextMessageStartEvent::new().with_timestamp(1234567890.0);
1853        let json = serde_json::to_string(&event).unwrap();
1854
1855        assert!(json.contains("\"timestamp\":1234567890.0"));
1856    }
1857
1858    #[test]
1859    fn test_thinking_text_message_content_event() {
1860        let event = ThinkingTextMessageContentEvent::new("Let me think about this...");
1861
1862        assert_eq!(event.delta, "Let me think about this...");
1863
1864        let json = serde_json::to_string(&event).unwrap();
1865        assert!(json.contains("\"delta\":\"Let me think about this...\""));
1866    }
1867
1868    #[test]
1869    fn test_thinking_text_message_content_event_allows_empty() {
1870        // Unlike TextMessageContentEvent, ThinkingTextMessageContentEvent allows empty delta
1871        let event = ThinkingTextMessageContentEvent::new("");
1872        assert_eq!(event.delta, "");
1873    }
1874
1875    #[test]
1876    fn test_thinking_text_message_end_event() {
1877        let event = ThinkingTextMessageEndEvent::new();
1878        let json = serde_json::to_string(&event).unwrap();
1879
1880        // Should be minimal
1881        assert_eq!(json, "{}");
1882    }
1883
1884    #[test]
1885    fn test_thinking_text_message_events_default() {
1886        let start = ThinkingTextMessageStartEvent::default();
1887        let end = ThinkingTextMessageEndEvent::default();
1888
1889        assert!(start.base.timestamp.is_none());
1890        assert!(end.base.timestamp.is_none());
1891    }
1892
1893    // =========================================================================
1894    // Tool Call Event Tests
1895    // =========================================================================
1896
1897    #[test]
1898    fn test_tool_call_start_event() {
1899        use crate::types::ToolCallId;
1900
1901        let event = ToolCallStartEvent::new(ToolCallId::random(), "get_weather");
1902
1903        assert_eq!(event.tool_call_name, "get_weather");
1904        assert!(event.parent_message_id.is_none());
1905
1906        let json = serde_json::to_string(&event).unwrap();
1907        assert!(json.contains("\"toolCallId\""));
1908        assert!(json.contains("\"toolCallName\":\"get_weather\""));
1909        assert!(!json.contains("parentMessageId")); // skipped when None
1910    }
1911
1912    #[test]
1913    fn test_tool_call_start_event_with_parent() {
1914        use crate::types::{MessageId, ToolCallId};
1915
1916        let event = ToolCallStartEvent::new(ToolCallId::random(), "get_weather")
1917            .with_parent_message_id(MessageId::random());
1918
1919        assert!(event.parent_message_id.is_some());
1920
1921        let json = serde_json::to_string(&event).unwrap();
1922        assert!(json.contains("\"parentMessageId\""));
1923    }
1924
1925    #[test]
1926    fn test_tool_call_args_event() {
1927        use crate::types::ToolCallId;
1928
1929        let event = ToolCallArgsEvent::new(ToolCallId::random(), r#"{"location":"#);
1930
1931        assert_eq!(event.delta, r#"{"location":"#);
1932
1933        let json = serde_json::to_string(&event).unwrap();
1934        assert!(json.contains("\"toolCallId\""));
1935        assert!(json.contains("\"delta\""));
1936    }
1937
1938    #[test]
1939    fn test_tool_call_end_event() {
1940        use crate::types::ToolCallId;
1941
1942        let event = ToolCallEndEvent::new(ToolCallId::random());
1943
1944        let json = serde_json::to_string(&event).unwrap();
1945        assert!(json.contains("\"toolCallId\""));
1946    }
1947
1948    #[test]
1949    fn test_tool_call_chunk_event() {
1950        use crate::types::ToolCallId;
1951
1952        let event = ToolCallChunkEvent::new()
1953            .with_tool_call_id(ToolCallId::random())
1954            .with_tool_call_name("search")
1955            .with_delta(r#"{"query": "rust"}"#);
1956
1957        assert!(event.tool_call_id.is_some());
1958        assert_eq!(event.tool_call_name, Some("search".to_string()));
1959        assert!(event.delta.is_some());
1960
1961        let json = serde_json::to_string(&event).unwrap();
1962        assert!(json.contains("\"toolCallId\""));
1963        assert!(json.contains("\"toolCallName\":\"search\""));
1964        assert!(json.contains("\"delta\""));
1965    }
1966
1967    #[test]
1968    fn test_tool_call_chunk_event_skips_none() {
1969        let event = ToolCallChunkEvent::new();
1970        let json = serde_json::to_string(&event).unwrap();
1971
1972        // Should not contain optional fields when None
1973        assert_eq!(json, "{}");
1974    }
1975
1976    #[test]
1977    fn test_tool_call_result_event() {
1978        use crate::types::{MessageId, Role, ToolCallId};
1979
1980        let event = ToolCallResultEvent::new(
1981            MessageId::random(),
1982            ToolCallId::random(),
1983            r#"{"weather": "sunny", "temp": 72}"#,
1984        );
1985
1986        assert_eq!(event.role, Role::Tool);
1987        assert!(event.content.contains("sunny"));
1988
1989        let json = serde_json::to_string(&event).unwrap();
1990        assert!(json.contains("\"messageId\""));
1991        assert!(json.contains("\"toolCallId\""));
1992        assert!(json.contains("\"content\""));
1993        assert!(json.contains("\"role\":\"tool\""));
1994    }
1995
1996    #[test]
1997    fn test_tool_call_result_event_deserialize_default_role() {
1998        // Test that role defaults to "tool" when not present in JSON
1999        let json = r#"{"messageId":"550e8400-e29b-41d4-a716-446655440000","toolCallId":"6ba7b810-9dad-11d1-80b4-00c04fd430c8","content":"result"}"#;
2000        let event: ToolCallResultEvent = serde_json::from_str(json).unwrap();
2001
2002        assert_eq!(event.role, Role::Tool);
2003    }
2004
2005    // =========================================================================
2006    // Run Lifecycle Event Tests
2007    // =========================================================================
2008
2009    #[test]
2010    fn test_run_started_event() {
2011        use crate::types::{RunId, ThreadId};
2012
2013        let event = RunStartedEvent::new(ThreadId::random(), RunId::random());
2014
2015        let json = serde_json::to_string(&event).unwrap();
2016        assert!(json.contains("\"threadId\""));
2017        assert!(json.contains("\"runId\""));
2018    }
2019
2020    #[test]
2021    fn test_run_finished_event() {
2022        use crate::types::{RunId, ThreadId};
2023
2024        let event = RunFinishedEvent::new(ThreadId::random(), RunId::random());
2025
2026        assert!(event.result.is_none());
2027
2028        let json = serde_json::to_string(&event).unwrap();
2029        assert!(json.contains("\"threadId\""));
2030        assert!(json.contains("\"runId\""));
2031        assert!(!json.contains("\"result\"")); // skipped when None
2032    }
2033
2034    #[test]
2035    fn test_run_finished_event_with_result() {
2036        use crate::types::{RunId, ThreadId};
2037
2038        let event = RunFinishedEvent::new(ThreadId::random(), RunId::random())
2039            .with_result(serde_json::json!({"success": true}));
2040
2041        assert!(event.result.is_some());
2042
2043        let json = serde_json::to_string(&event).unwrap();
2044        assert!(json.contains("\"result\""));
2045        assert!(json.contains("\"success\":true"));
2046    }
2047
2048    #[test]
2049    fn test_run_error_event() {
2050        let event = RunErrorEvent::new("Connection timeout");
2051
2052        assert_eq!(event.message, "Connection timeout");
2053        assert!(event.code.is_none());
2054
2055        let json = serde_json::to_string(&event).unwrap();
2056        assert!(json.contains("\"message\":\"Connection timeout\""));
2057        assert!(!json.contains("\"code\"")); // skipped when None
2058    }
2059
2060    #[test]
2061    fn test_run_error_event_with_code() {
2062        let event = RunErrorEvent::new("Rate limit exceeded").with_code("RATE_LIMITED");
2063
2064        assert_eq!(event.code, Some("RATE_LIMITED".to_string()));
2065
2066        let json = serde_json::to_string(&event).unwrap();
2067        assert!(json.contains("\"code\":\"RATE_LIMITED\""));
2068    }
2069
2070    // =========================================================================
2071    // Interrupt Tests
2072    // =========================================================================
2073
2074    #[test]
2075    fn test_run_finished_outcome_serialization() {
2076        // Test SCREAMING_SNAKE_CASE serialization
2077        let success = RunFinishedOutcome::Success;
2078        let interrupt = RunFinishedOutcome::Interrupt;
2079
2080        let success_json = serde_json::to_string(&success).unwrap();
2081        let interrupt_json = serde_json::to_string(&interrupt).unwrap();
2082
2083        assert_eq!(success_json, "\"SUCCESS\"");
2084        assert_eq!(interrupt_json, "\"INTERRUPT\"");
2085
2086        // Test deserialization
2087        let deserialized: RunFinishedOutcome = serde_json::from_str("\"SUCCESS\"").unwrap();
2088        assert_eq!(deserialized, RunFinishedOutcome::Success);
2089
2090        let deserialized: RunFinishedOutcome = serde_json::from_str("\"INTERRUPT\"").unwrap();
2091        assert_eq!(deserialized, RunFinishedOutcome::Interrupt);
2092    }
2093
2094    #[test]
2095    fn test_run_finished_outcome_default() {
2096        let outcome = RunFinishedOutcome::default();
2097        assert_eq!(outcome, RunFinishedOutcome::Success);
2098    }
2099
2100    #[test]
2101    fn test_interrupt_info_empty() {
2102        let info = InterruptInfo::new();
2103
2104        assert!(info.id.is_none());
2105        assert!(info.reason.is_none());
2106        assert!(info.payload.is_none());
2107
2108        // Empty struct should serialize to {}
2109        let json = serde_json::to_string(&info).unwrap();
2110        assert_eq!(json, "{}");
2111    }
2112
2113    #[test]
2114    fn test_interrupt_info_with_all_fields() {
2115        let info = InterruptInfo::new()
2116            .with_id("approval-001")
2117            .with_reason("human_approval")
2118            .with_payload(serde_json::json!({"action": "delete", "rows": 42}));
2119
2120        assert_eq!(info.id, Some("approval-001".to_string()));
2121        assert_eq!(info.reason, Some("human_approval".to_string()));
2122        assert!(info.payload.is_some());
2123
2124        let json = serde_json::to_string(&info).unwrap();
2125        assert!(json.contains("\"id\":\"approval-001\""));
2126        assert!(json.contains("\"reason\":\"human_approval\""));
2127        assert!(json.contains("\"action\":\"delete\""));
2128    }
2129
2130    #[test]
2131    fn test_run_finished_event_with_interrupt() {
2132        use crate::types::{RunId, ThreadId};
2133
2134        let event = RunFinishedEvent::new(ThreadId::random(), RunId::random())
2135            .with_interrupt(
2136                InterruptInfo::new()
2137                    .with_reason("human_approval")
2138                    .with_payload(serde_json::json!({"proposal": "send email"}))
2139            );
2140
2141        // with_interrupt sets outcome to Interrupt
2142        assert_eq!(event.outcome, Some(RunFinishedOutcome::Interrupt));
2143        assert!(event.interrupt.is_some());
2144        assert!(event.result.is_none());
2145
2146        let json = serde_json::to_string(&event).unwrap();
2147        assert!(json.contains("\"outcome\":\"INTERRUPT\""));
2148        assert!(json.contains("\"interrupt\""));
2149        assert!(json.contains("\"reason\":\"human_approval\""));
2150    }
2151
2152    #[test]
2153    fn test_run_finished_event_backward_compatibility() {
2154        use crate::types::{RunId, ThreadId};
2155
2156        // Old-style event without outcome field
2157        let event = RunFinishedEvent::new(ThreadId::random(), RunId::random())
2158            .with_result(serde_json::json!({"done": true}));
2159
2160        // outcome is None (backward compat)
2161        assert!(event.outcome.is_none());
2162        assert!(event.interrupt.is_none());
2163
2164        // effective_outcome should infer Success
2165        assert_eq!(event.effective_outcome(), RunFinishedOutcome::Success);
2166
2167        let json = serde_json::to_string(&event).unwrap();
2168        assert!(!json.contains("\"outcome\"")); // skipped when None
2169    }
2170
2171    #[test]
2172    fn test_run_finished_event_effective_outcome() {
2173        use crate::types::{RunId, ThreadId};
2174
2175        // No outcome, no interrupt → Success
2176        let event1 = RunFinishedEvent::new(ThreadId::random(), RunId::random());
2177        assert_eq!(event1.effective_outcome(), RunFinishedOutcome::Success);
2178
2179        // No outcome, has interrupt → Interrupt
2180        let mut event2 = RunFinishedEvent::new(ThreadId::random(), RunId::random());
2181        event2.interrupt = Some(InterruptInfo::new());
2182        assert_eq!(event2.effective_outcome(), RunFinishedOutcome::Interrupt);
2183
2184        // Explicit outcome overrides
2185        let event3 = RunFinishedEvent::new(ThreadId::random(), RunId::random())
2186            .with_outcome(RunFinishedOutcome::Interrupt);
2187        assert_eq!(event3.effective_outcome(), RunFinishedOutcome::Interrupt);
2188    }
2189
2190    // =========================================================================
2191    // Step Event Tests
2192    // =========================================================================
2193
2194    #[test]
2195    fn test_step_started_event() {
2196        let event = StepStartedEvent::new("process_input");
2197
2198        assert_eq!(event.step_name, "process_input");
2199
2200        let json = serde_json::to_string(&event).unwrap();
2201        assert!(json.contains("\"stepName\":\"process_input\""));
2202    }
2203
2204    #[test]
2205    fn test_step_finished_event() {
2206        let event = StepFinishedEvent::new("generate_response");
2207
2208        assert_eq!(event.step_name, "generate_response");
2209
2210        let json = serde_json::to_string(&event).unwrap();
2211        assert!(json.contains("\"stepName\":\"generate_response\""));
2212    }
2213
2214    #[test]
2215    fn test_step_events_with_timestamp() {
2216        let start = StepStartedEvent::new("step1").with_timestamp(1234567890.0);
2217        let end = StepFinishedEvent::new("step1").with_timestamp(1234567891.0);
2218
2219        assert_eq!(start.base.timestamp, Some(1234567890.0));
2220        assert_eq!(end.base.timestamp, Some(1234567891.0));
2221    }
2222
2223    // =========================================================================
2224    // State Event Tests
2225    // =========================================================================
2226
2227    #[test]
2228    fn test_state_snapshot_event() {
2229        let event = StateSnapshotEvent::new(serde_json::json!({"count": 42}));
2230
2231        let json = serde_json::to_string(&event).unwrap();
2232        assert!(json.contains("\"snapshot\""));
2233        assert!(json.contains("\"count\":42"));
2234    }
2235
2236    #[test]
2237    fn test_state_snapshot_event_default() {
2238        let event: StateSnapshotEvent<()> = StateSnapshotEvent::default();
2239        assert!(event.base.timestamp.is_none());
2240    }
2241
2242    #[test]
2243    fn test_state_delta_event() {
2244        let patches = vec![
2245            serde_json::json!({"op": "replace", "path": "/count", "value": 43}),
2246            serde_json::json!({"op": "add", "path": "/new_field", "value": "hello"}),
2247        ];
2248        let event = StateDeltaEvent::new(patches);
2249
2250        assert_eq!(event.delta.len(), 2);
2251
2252        let json = serde_json::to_string(&event).unwrap();
2253        assert!(json.contains("\"delta\""));
2254        assert!(json.contains("\"op\":\"replace\""));
2255    }
2256
2257    #[test]
2258    fn test_state_delta_event_default() {
2259        let event = StateDeltaEvent::default();
2260        assert!(event.delta.is_empty());
2261    }
2262
2263    #[test]
2264    fn test_messages_snapshot_event() {
2265        use crate::types::{Message, MessageId};
2266
2267        let messages = vec![
2268            Message::User {
2269                id: MessageId::random(),
2270                content: "Hello".to_string(),
2271                name: None,
2272            },
2273            Message::Assistant {
2274                id: MessageId::random(),
2275                content: Some("Hi there!".to_string()),
2276                name: None,
2277                tool_calls: None,
2278            },
2279        ];
2280        let event = MessagesSnapshotEvent::new(messages);
2281
2282        assert_eq!(event.messages.len(), 2);
2283
2284        let json = serde_json::to_string(&event).unwrap();
2285        assert!(json.contains("\"messages\""));
2286    }
2287
2288    #[test]
2289    fn test_messages_snapshot_event_default() {
2290        let event = MessagesSnapshotEvent::default();
2291        assert!(event.messages.is_empty());
2292    }
2293
2294    // =========================================================================
2295    // Thinking Step Event Tests
2296    // =========================================================================
2297
2298    #[test]
2299    fn test_thinking_start_event() {
2300        let event = ThinkingStartEvent::new();
2301
2302        assert!(event.title.is_none());
2303
2304        let json = serde_json::to_string(&event).unwrap();
2305        assert!(!json.contains("\"title\"")); // skipped when None
2306    }
2307
2308    #[test]
2309    fn test_thinking_start_event_with_title() {
2310        let event = ThinkingStartEvent::new().with_title("Analyzing query");
2311
2312        assert_eq!(event.title, Some("Analyzing query".to_string()));
2313
2314        let json = serde_json::to_string(&event).unwrap();
2315        assert!(json.contains("\"title\":\"Analyzing query\""));
2316    }
2317
2318    #[test]
2319    fn test_thinking_end_event() {
2320        let event = ThinkingEndEvent::new();
2321
2322        let json = serde_json::to_string(&event).unwrap();
2323        assert_eq!(json, "{}");
2324    }
2325
2326    #[test]
2327    fn test_thinking_step_events_default() {
2328        let start = ThinkingStartEvent::default();
2329        let end = ThinkingEndEvent::default();
2330
2331        assert!(start.title.is_none());
2332        assert!(end.base.timestamp.is_none());
2333    }
2334
2335    // =========================================================================
2336    // Special Event Tests
2337    // =========================================================================
2338
2339    #[test]
2340    fn test_raw_event() {
2341        let event = RawEvent::new(serde_json::json!({"provider_data": "openai"}));
2342
2343        assert!(event.source.is_none());
2344
2345        let json = serde_json::to_string(&event).unwrap();
2346        assert!(json.contains("\"event\""));
2347        assert!(json.contains("\"provider_data\":\"openai\""));
2348        assert!(!json.contains("\"source\"")); // skipped when None
2349    }
2350
2351    #[test]
2352    fn test_raw_event_with_source() {
2353        let event = RawEvent::new(serde_json::json!({})).with_source("anthropic");
2354
2355        assert_eq!(event.source, Some("anthropic".to_string()));
2356
2357        let json = serde_json::to_string(&event).unwrap();
2358        assert!(json.contains("\"source\":\"anthropic\""));
2359    }
2360
2361    #[test]
2362    fn test_custom_event() {
2363        let event = CustomEvent::new("user_action", serde_json::json!({"clicked": "button"}));
2364
2365        assert_eq!(event.name, "user_action");
2366
2367        let json = serde_json::to_string(&event).unwrap();
2368        assert!(json.contains("\"name\":\"user_action\""));
2369        assert!(json.contains("\"value\""));
2370        assert!(json.contains("\"clicked\":\"button\""));
2371    }
2372
2373    // =========================================================================
2374    // Event Enum Tests
2375    // =========================================================================
2376
2377    #[test]
2378    fn test_event_enum_serialization() {
2379        use crate::types::MessageId;
2380
2381        let event: Event = Event::TextMessageStart(TextMessageStartEvent::new(
2382            MessageId::random(),
2383        ));
2384
2385        let json = serde_json::to_string(&event).unwrap();
2386        assert!(json.contains("\"type\":\"TEXT_MESSAGE_START\""));
2387        assert!(json.contains("\"messageId\""));
2388        assert!(json.contains("\"role\":\"assistant\""));
2389    }
2390
2391    #[test]
2392    fn test_event_enum_deserialization() {
2393        let json = r#"{"type":"RUN_ERROR","message":"Test error"}"#;
2394        let event: Event = serde_json::from_str(json).unwrap();
2395
2396        match event {
2397            Event::RunError(e) => assert_eq!(e.message, "Test error"),
2398            _ => panic!("Expected RunError variant"),
2399        }
2400    }
2401
2402    #[test]
2403    fn test_event_type_method() {
2404        use crate::types::MessageId;
2405
2406        let event: Event = Event::TextMessageEnd(TextMessageEndEvent::new(MessageId::random()));
2407        assert_eq!(event.event_type(), EventType::TextMessageEnd);
2408
2409        let event: Event = Event::RunStarted(RunStartedEvent::new(
2410            crate::types::ThreadId::random(),
2411            crate::types::RunId::random(),
2412        ));
2413        assert_eq!(event.event_type(), EventType::RunStarted);
2414
2415        let event: Event = Event::Custom(CustomEvent::new("test", serde_json::json!({})));
2416        assert_eq!(event.event_type(), EventType::Custom);
2417    }
2418
2419    #[test]
2420    fn test_event_timestamp_method() {
2421        use crate::types::MessageId;
2422
2423        let event: Event = Event::TextMessageStart(
2424            TextMessageStartEvent::new(MessageId::random())
2425                .with_timestamp(1234567890.0),
2426        );
2427        assert_eq!(event.timestamp(), Some(1234567890.0));
2428
2429        let event: Event = Event::ThinkingEnd(ThinkingEndEvent::new());
2430        assert_eq!(event.timestamp(), None);
2431    }
2432
2433    #[test]
2434    fn test_event_all_variants_serialize() {
2435        use crate::types::{Message, MessageId, RunId, ThreadId, ToolCallId};
2436
2437        // Test that all event variants can be serialized
2438        let events: Vec<Event> = vec![
2439            Event::TextMessageStart(TextMessageStartEvent::new(MessageId::random())),
2440            Event::TextMessageContent(TextMessageContentEvent::new_unchecked(MessageId::random(), "Hello")),
2441            Event::TextMessageEnd(TextMessageEndEvent::new(MessageId::random())),
2442            Event::TextMessageChunk(TextMessageChunkEvent::new(Role::Assistant).with_delta("Hi")),
2443            Event::ThinkingTextMessageStart(ThinkingTextMessageStartEvent::new()),
2444            Event::ThinkingTextMessageContent(ThinkingTextMessageContentEvent::new("thinking...")),
2445            Event::ThinkingTextMessageEnd(ThinkingTextMessageEndEvent::new()),
2446            Event::ToolCallStart(ToolCallStartEvent::new(ToolCallId::random(), "test_tool")),
2447            Event::ToolCallArgs(ToolCallArgsEvent::new(ToolCallId::random(), "{}")),
2448            Event::ToolCallEnd(ToolCallEndEvent::new(ToolCallId::random())),
2449            Event::ToolCallChunk(ToolCallChunkEvent::new()),
2450            Event::ToolCallResult(ToolCallResultEvent::new(MessageId::random(), ToolCallId::random(), "result")),
2451            Event::ThinkingStart(ThinkingStartEvent::new()),
2452            Event::ThinkingEnd(ThinkingEndEvent::new()),
2453            Event::StateSnapshot(StateSnapshotEvent::new(serde_json::json!({}))),
2454            Event::StateDelta(StateDeltaEvent::new(vec![])),
2455            Event::MessagesSnapshot(MessagesSnapshotEvent::new(vec![Message::Assistant {
2456                id: MessageId::random(),
2457                content: Some("Hi".to_string()),
2458                name: None,
2459                tool_calls: None,
2460            }])),
2461            Event::ActivitySnapshot(ActivitySnapshotEvent::new(MessageId::random(), "PLAN", serde_json::json!({"steps": []}))),
2462            Event::ActivityDelta(ActivityDeltaEvent::new(MessageId::random(), "PLAN", vec![serde_json::json!({"op": "add", "path": "/steps/-", "value": "test"})])),
2463            Event::Raw(RawEvent::new(serde_json::json!({}))),
2464            Event::Custom(CustomEvent::new("test", serde_json::json!({}))),
2465            Event::RunStarted(RunStartedEvent::new(ThreadId::random(), RunId::random())),
2466            Event::RunFinished(RunFinishedEvent::new(ThreadId::random(), RunId::random())),
2467            Event::RunError(RunErrorEvent::new("error")),
2468            Event::StepStarted(StepStartedEvent::new("step")),
2469            Event::StepFinished(StepFinishedEvent::new("step")),
2470        ];
2471
2472        for event in events {
2473            let json = serde_json::to_string(&event).unwrap();
2474            assert!(json.contains("\"type\":"));
2475
2476            // Roundtrip test
2477            let deserialized: Event = serde_json::from_str(&json).unwrap();
2478            assert_eq!(event.event_type(), deserialized.event_type());
2479        }
2480    }
2481}