Skip to main content

opi_agent/
event.rs

1//! Agent event protocol (S7.4).
2
3use serde::{Deserialize, Serialize};
4
5use crate::message::AgentMessage;
6use crate::session_event::{CompactionReason, CompactionResult};
7
8/// Callback type for emitting agent events to subscribers.
9pub type AgentEventSink = Box<dyn Fn(AgentEvent) + Send + Sync>;
10
11/// Events emitted during the agent loop lifecycle.
12#[non_exhaustive]
13#[derive(Debug, Clone, Serialize, Deserialize)]
14#[serde(tag = "type")]
15pub enum AgentEvent {
16    /// The agent loop has started.
17    AgentStart,
18    /// The agent loop has ended. No more loop events will be emitted.
19    AgentEnd { messages: Vec<AgentMessage> },
20    /// A new turn (provider request/response cycle) has started.
21    TurnStart,
22    /// A turn has ended with the assistant message and any tool results.
23    TurnEnd {
24        message: AgentMessage,
25        tool_results: Vec<opi_ai::message::ToolResultMessage>,
26    },
27    /// An assistant message has started streaming.
28    MessageStart { message: AgentMessage },
29    /// An assistant message has been updated with a stream event.
30    MessageUpdate {
31        message: AgentMessage,
32        #[serde(
33            serialize_with = "serde_json_bridge::serialize",
34            deserialize_with = "deserialize_boxed_stream_event"
35        )]
36        assistant_event: Box<opi_ai::stream::AssistantStreamEvent>,
37    },
38    /// An assistant message has finished streaming.
39    MessageEnd { message: AgentMessage },
40    /// Tool execution is about to begin.
41    ToolExecutionStart {
42        tool_call_id: String,
43        tool_name: String,
44        args: serde_json::Value,
45    },
46    /// Tool execution has produced a progress update.
47    ToolExecutionUpdate {
48        tool_call_id: String,
49        tool_name: String,
50        args: serde_json::Value,
51        partial_result: serde_json::Value,
52    },
53    /// Tool execution has completed.
54    ToolExecutionEnd {
55        tool_call_id: String,
56        tool_name: String,
57        result: serde_json::Value,
58        details: Option<serde_json::Value>,
59        is_error: bool,
60    },
61    /// Queue messages were delivered to the conversation.
62    QueueUpdate {
63        steering: Vec<String>,
64        follow_up: Vec<String>,
65    },
66    /// A retryable provider error occurred; a retry attempt is starting.
67    AutoRetryStart {
68        attempt: u32,
69        max_attempts: u32,
70        delay_ms: u64,
71        error_message: String,
72    },
73    /// A retry attempt concluded (either successfully or after exhausting attempts).
74    AutoRetryEnd {
75        success: bool,
76        attempt: u32,
77        final_error: Option<String>,
78    },
79    /// Compaction has started. Emitted by the harness outside the agent loop.
80    CompactionStart { reason: CompactionReason },
81    /// Compaction has finished. Emitted by the harness outside the agent loop.
82    CompactionEnd {
83        reason: CompactionReason,
84        result: Option<CompactionResult>,
85        aborted: bool,
86        error_message: Option<String>,
87    },
88    /// Session persistence failed (disk full, permissions, etc.).
89    SessionPersistError { message: String },
90}
91
92fn deserialize_boxed_stream_event<'de, D>(
93    deserializer: D,
94) -> Result<Box<opi_ai::stream::AssistantStreamEvent>, D::Error>
95where
96    D: serde::Deserializer<'de>,
97{
98    let v = serde_json::Value::deserialize(deserializer)?;
99    let event: opi_ai::stream::AssistantStreamEvent =
100        serde_json::from_value(v).map_err(serde::de::Error::custom)?;
101    Ok(Box::new(event))
102}
103
104mod serde_json_bridge {
105    use serde::{Serialize as _, Serializer};
106
107    pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
108    where
109        T: serde::Serialize,
110        S: Serializer,
111    {
112        serde_json::to_value(value)
113            .map_err(serde::ser::Error::custom)
114            .and_then(|v| v.serialize(serializer))
115    }
116}