Skip to main content

systemprompt_models/events/
a2a_event.rs

1//! A2A protocol events emitted over the event bus and SSE streams.
2//!
3//! [`A2AEvent`] is the timestamped, tagged union of every agent-to-agent
4//! lifecycle event (task submission, status updates, artifacts, agent
5//! messages, input/auth prompts, JSON-RPC responses). [`A2AEventBuilder`]
6//! stamps each variant with the current time at construction.
7//!
8//! Copyright (c) systemprompt.io — Business Source License 1.1.
9//! See <https://systemprompt.io> for licensing details.
10
11use chrono::{DateTime, Utc};
12use serde::{Deserialize, Serialize};
13use systemprompt_identifiers::{ContextId, MessageId, TaskId};
14
15use super::a2a_event_type::A2AEventType;
16use super::payloads::a2a::{
17    AgentMessagePayload, ArtifactCreatedPayload, ArtifactUpdatedPayload, AuthRequiredPayload,
18    InputRequiredPayload, JsonRpcErrorPayload, JsonRpcResponsePayload, TaskStatusUpdatePayload,
19    TaskSubmittedPayload,
20};
21use crate::a2a::{Artifact, TaskState};
22
23#[derive(Debug, Clone, Serialize, Deserialize)]
24#[serde(tag = "type", rename_all = "SCREAMING_SNAKE_CASE")]
25pub enum A2AEvent {
26    TaskSubmitted {
27        timestamp: DateTime<Utc>,
28        #[serde(flatten)]
29        payload: TaskSubmittedPayload,
30    },
31    TaskStatusUpdate {
32        timestamp: DateTime<Utc>,
33        #[serde(flatten)]
34        payload: TaskStatusUpdatePayload,
35    },
36    ArtifactCreated {
37        timestamp: DateTime<Utc>,
38        #[serde(flatten)]
39        payload: Box<ArtifactCreatedPayload>,
40    },
41    ArtifactUpdated {
42        timestamp: DateTime<Utc>,
43        #[serde(flatten)]
44        payload: ArtifactUpdatedPayload,
45    },
46    AgentMessage {
47        timestamp: DateTime<Utc>,
48        #[serde(flatten)]
49        payload: AgentMessagePayload,
50    },
51    InputRequired {
52        timestamp: DateTime<Utc>,
53        #[serde(flatten)]
54        payload: InputRequiredPayload,
55    },
56    AuthRequired {
57        timestamp: DateTime<Utc>,
58        #[serde(flatten)]
59        payload: AuthRequiredPayload,
60    },
61    JsonRpcResponse {
62        timestamp: DateTime<Utc>,
63        #[serde(flatten)]
64        payload: JsonRpcResponsePayload,
65    },
66    JsonRpcError {
67        timestamp: DateTime<Utc>,
68        #[serde(flatten)]
69        payload: JsonRpcErrorPayload,
70    },
71}
72
73impl A2AEvent {
74    pub const fn event_type(&self) -> A2AEventType {
75        match self {
76            Self::TaskSubmitted { .. } => A2AEventType::TaskSubmitted,
77            Self::TaskStatusUpdate { .. } => A2AEventType::TaskStatusUpdate,
78            Self::ArtifactCreated { .. } => A2AEventType::ArtifactCreated,
79            Self::ArtifactUpdated { .. } => A2AEventType::ArtifactUpdated,
80            Self::AgentMessage { .. } => A2AEventType::AgentMessage,
81            Self::InputRequired { .. } => A2AEventType::InputRequired,
82            Self::AuthRequired { .. } => A2AEventType::AuthRequired,
83            Self::JsonRpcResponse { .. } => A2AEventType::JsonRpcResponse,
84            Self::JsonRpcError { .. } => A2AEventType::JsonRpcError,
85        }
86    }
87
88    pub const fn timestamp(&self) -> DateTime<Utc> {
89        match self {
90            Self::TaskSubmitted { timestamp, .. }
91            | Self::TaskStatusUpdate { timestamp, .. }
92            | Self::ArtifactCreated { timestamp, .. }
93            | Self::ArtifactUpdated { timestamp, .. }
94            | Self::AgentMessage { timestamp, .. }
95            | Self::InputRequired { timestamp, .. }
96            | Self::AuthRequired { timestamp, .. }
97            | Self::JsonRpcResponse { timestamp, .. }
98            | Self::JsonRpcError { timestamp, .. } => *timestamp,
99        }
100    }
101}
102
103#[derive(Debug, Clone, Copy)]
104pub struct A2AEventBuilder;
105
106impl A2AEventBuilder {
107    pub fn task_submitted(
108        task_id: TaskId,
109        context_id: ContextId,
110        agent_name: String,
111        input: Option<serde_json::Value>,
112    ) -> A2AEvent {
113        A2AEvent::TaskSubmitted {
114            timestamp: Utc::now(),
115            payload: TaskSubmittedPayload {
116                task_id,
117                context_id,
118                agent_name,
119                input,
120            },
121        }
122    }
123
124    pub fn task_status_update(
125        task_id: TaskId,
126        context_id: ContextId,
127        state: TaskState,
128        message: Option<String>,
129    ) -> A2AEvent {
130        A2AEvent::TaskStatusUpdate {
131            timestamp: Utc::now(),
132            payload: TaskStatusUpdatePayload {
133                task_id,
134                context_id,
135                state,
136                message,
137            },
138        }
139    }
140
141    pub fn artifact_created(
142        task_id: TaskId,
143        context_id: ContextId,
144        artifact: Artifact,
145    ) -> A2AEvent {
146        A2AEvent::ArtifactCreated {
147            timestamp: Utc::now(),
148            payload: Box::new(ArtifactCreatedPayload {
149                task_id,
150                context_id,
151                artifact,
152            }),
153        }
154    }
155
156    pub fn agent_message(
157        task_id: TaskId,
158        context_id: ContextId,
159        message_id: MessageId,
160        content: String,
161    ) -> A2AEvent {
162        A2AEvent::AgentMessage {
163            timestamp: Utc::now(),
164            payload: AgentMessagePayload {
165                task_id,
166                context_id,
167                message_id,
168                content,
169            },
170        }
171    }
172
173    pub fn input_required(task_id: TaskId, context_id: ContextId, prompt: String) -> A2AEvent {
174        A2AEvent::InputRequired {
175            timestamp: Utc::now(),
176            payload: InputRequiredPayload {
177                task_id,
178                context_id,
179                prompt,
180            },
181        }
182    }
183
184    pub fn auth_required(task_id: TaskId, context_id: ContextId, auth_url: String) -> A2AEvent {
185        A2AEvent::AuthRequired {
186            timestamp: Utc::now(),
187            payload: AuthRequiredPayload {
188                task_id,
189                context_id,
190                auth_url,
191            },
192        }
193    }
194
195    pub fn json_rpc_response(id: serde_json::Value, result: serde_json::Value) -> A2AEvent {
196        A2AEvent::JsonRpcResponse {
197            timestamp: Utc::now(),
198            payload: JsonRpcResponsePayload { id, result },
199        }
200    }
201}