Skip to main content

systemprompt_models/a2a/
task.rs

1//! A2A task, status, and state wire types.
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use super::artifact::Artifact;
7use super::message::Message;
8use super::task_metadata::TaskMetadata;
9use serde::{Deserialize, Serialize};
10
11use crate::errors::ParseEnumError;
12use systemprompt_identifiers::{ContextId, TaskId};
13
14#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
15#[serde(rename_all = "camelCase")]
16pub struct Task {
17    pub id: TaskId,
18    pub context_id: ContextId,
19    pub status: TaskStatus,
20    pub history: Option<Vec<Message>>,
21    pub artifacts: Option<Vec<Artifact>>,
22    pub metadata: Option<TaskMetadata>,
23    #[serde(skip_serializing_if = "Option::is_none")]
24    pub created_at: Option<chrono::DateTime<chrono::Utc>>,
25    #[serde(skip_serializing_if = "Option::is_none")]
26    pub last_modified: Option<chrono::DateTime<chrono::Utc>>,
27}
28
29impl Default for Task {
30    fn default() -> Self {
31        let now = chrono::Utc::now();
32        Self {
33            id: TaskId::generate(),
34            context_id: ContextId::generate(),
35            status: TaskStatus::default(),
36            history: None,
37            artifacts: None,
38            metadata: None,
39            created_at: Some(now),
40            last_modified: Some(now),
41        }
42    }
43}
44
45#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
46pub struct TaskStatus {
47    pub state: TaskState,
48    pub message: Option<Message>,
49    pub timestamp: Option<chrono::DateTime<chrono::Utc>>,
50}
51
52impl Default for TaskStatus {
53    fn default() -> Self {
54        Self {
55            state: TaskState::Submitted,
56            message: None,
57            timestamp: None,
58        }
59    }
60}
61
62#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
63pub enum TaskState {
64    #[serde(rename = "TASK_STATE_PENDING")]
65    Pending,
66    #[serde(rename = "TASK_STATE_SUBMITTED")]
67    Submitted,
68    #[serde(rename = "TASK_STATE_WORKING")]
69    Working,
70    #[serde(rename = "TASK_STATE_COMPLETED")]
71    Completed,
72    #[serde(rename = "TASK_STATE_FAILED")]
73    Failed,
74    #[serde(rename = "TASK_STATE_CANCELED")]
75    Canceled,
76    #[serde(rename = "TASK_STATE_REJECTED")]
77    Rejected,
78    #[serde(rename = "TASK_STATE_INPUT_REQUIRED")]
79    InputRequired,
80    #[serde(rename = "TASK_STATE_AUTH_REQUIRED")]
81    AuthRequired,
82    #[serde(rename = "TASK_STATE_UNKNOWN")]
83    Unknown,
84}
85
86impl TaskState {
87    pub const fn as_str(self) -> &'static str {
88        match self {
89            Self::Pending => "TASK_STATE_PENDING",
90            Self::Submitted => "TASK_STATE_SUBMITTED",
91            Self::Working => "TASK_STATE_WORKING",
92            Self::Completed => "TASK_STATE_COMPLETED",
93            Self::Failed => "TASK_STATE_FAILED",
94            Self::Canceled => "TASK_STATE_CANCELED",
95            Self::Rejected => "TASK_STATE_REJECTED",
96            Self::InputRequired => "TASK_STATE_INPUT_REQUIRED",
97            Self::AuthRequired => "TASK_STATE_AUTH_REQUIRED",
98            Self::Unknown => "TASK_STATE_UNKNOWN",
99        }
100    }
101
102    pub const fn is_terminal(&self) -> bool {
103        matches!(
104            self,
105            Self::Completed | Self::Failed | Self::Canceled | Self::Rejected
106        )
107    }
108
109    pub const fn can_transition_to(&self, target: &Self) -> bool {
110        if self.is_terminal() {
111            return false;
112        }
113        match self {
114            Self::Pending => matches!(target, Self::Submitted),
115            Self::Submitted => matches!(
116                target,
117                Self::Working
118                    | Self::Completed
119                    | Self::Failed
120                    | Self::Canceled
121                    | Self::Rejected
122                    | Self::AuthRequired
123            ),
124            Self::Working => matches!(
125                target,
126                Self::Completed | Self::Failed | Self::Canceled | Self::InputRequired
127            ),
128            Self::InputRequired => matches!(
129                target,
130                Self::Working | Self::Completed | Self::Failed | Self::Canceled
131            ),
132            Self::AuthRequired => matches!(target, Self::Working | Self::Failed | Self::Canceled),
133            _ => false,
134        }
135    }
136}
137
138impl std::str::FromStr for TaskState {
139    type Err = ParseEnumError;
140
141    fn from_str(s: &str) -> Result<Self, Self::Err> {
142        match s {
143            "TASK_STATE_PENDING" | "pending" => Ok(Self::Pending),
144            "TASK_STATE_SUBMITTED" | "submitted" => Ok(Self::Submitted),
145            "TASK_STATE_WORKING" | "working" => Ok(Self::Working),
146            "TASK_STATE_COMPLETED" | "completed" => Ok(Self::Completed),
147            "TASK_STATE_FAILED" | "failed" => Ok(Self::Failed),
148            "TASK_STATE_CANCELED" | "canceled" => Ok(Self::Canceled),
149            "TASK_STATE_REJECTED" | "rejected" => Ok(Self::Rejected),
150            "TASK_STATE_INPUT_REQUIRED" | "input-required" => Ok(Self::InputRequired),
151            "TASK_STATE_AUTH_REQUIRED" | "auth-required" => Ok(Self::AuthRequired),
152            "TASK_STATE_UNKNOWN" | "unknown" => Ok(Self::Unknown),
153            _ => Err(ParseEnumError::new("task state", s)),
154        }
155    }
156}