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