Skip to main content

a2a_rust/types/
task.rs

1use serde::{Deserialize, Serialize};
2
3use crate::types::JsonObject;
4
5use super::message::{Artifact, Message};
6
7/// Server-side task resource.
8#[derive(Debug, Clone, Serialize, Deserialize)]
9#[serde(rename_all = "camelCase")]
10pub struct Task {
11    /// Unique task identifier.
12    pub id: String,
13    #[serde(default, skip_serializing_if = "Option::is_none")]
14    /// Context identifier shared with related messages and updates.
15    pub context_id: Option<String>,
16    /// Current task status.
17    pub status: TaskStatus,
18    #[serde(default, skip_serializing_if = "Vec::is_empty")]
19    /// Artifacts produced by the task.
20    pub artifacts: Vec<Artifact>,
21    #[serde(default, skip_serializing_if = "Vec::is_empty")]
22    /// Task message history.
23    pub history: Vec<Message>,
24    #[serde(default, skip_serializing_if = "Option::is_none")]
25    /// Optional task metadata.
26    pub metadata: Option<JsonObject>,
27}
28
29/// Snapshot of a task's current state.
30#[derive(Debug, Clone, Serialize, Deserialize)]
31#[serde(rename_all = "camelCase")]
32pub struct TaskStatus {
33    /// Current lifecycle state.
34    pub state: TaskState,
35    #[serde(default, skip_serializing_if = "Option::is_none")]
36    /// Optional status message payload.
37    pub message: Option<Message>,
38    #[serde(default, skip_serializing_if = "Option::is_none")]
39    /// Optional RFC 3339 timestamp for the status update.
40    pub timestamp: Option<String>,
41}
42
43/// Task lifecycle state.
44#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
45pub enum TaskState {
46    #[default]
47    #[serde(rename = "TASK_STATE_UNSPECIFIED")]
48    /// Unspecified state.
49    Unspecified,
50    #[serde(rename = "TASK_STATE_SUBMITTED")]
51    /// Task accepted but not yet running.
52    Submitted,
53    #[serde(rename = "TASK_STATE_WORKING")]
54    /// Task is currently running.
55    Working,
56    #[serde(rename = "TASK_STATE_COMPLETED")]
57    /// Task completed successfully.
58    Completed,
59    #[serde(rename = "TASK_STATE_FAILED")]
60    /// Task failed permanently.
61    Failed,
62    #[serde(rename = "TASK_STATE_CANCELED")]
63    /// Task was canceled.
64    Canceled,
65    #[serde(rename = "TASK_STATE_INPUT_REQUIRED")]
66    /// Task requires further user input.
67    InputRequired,
68    #[serde(rename = "TASK_STATE_REJECTED")]
69    /// Task was rejected before execution.
70    Rejected,
71    #[serde(rename = "TASK_STATE_AUTH_REQUIRED")]
72    /// Task requires authentication before continuing.
73    AuthRequired,
74}
75
76#[cfg(test)]
77mod tests {
78    use super::{Task, TaskState, TaskStatus};
79    use crate::types::{Message, Part, Role};
80
81    #[test]
82    fn task_state_serializes_as_proto_enum_name() {
83        let json =
84            serde_json::to_string(&TaskState::Completed).expect("task state should serialize");
85        assert_eq!(json, r#""TASK_STATE_COMPLETED""#);
86    }
87
88    #[test]
89    fn task_round_trip_serialization() {
90        let task = Task {
91            id: "task-1".to_owned(),
92            context_id: Some("ctx-1".to_owned()),
93            status: TaskStatus {
94                state: TaskState::Completed,
95                message: Some(Message {
96                    message_id: "msg-1".to_owned(),
97                    context_id: Some("ctx-1".to_owned()),
98                    task_id: Some("task-1".to_owned()),
99                    role: Role::Agent,
100                    parts: vec![Part {
101                        text: Some("done".to_owned()),
102                        raw: None,
103                        url: None,
104                        data: None,
105                        metadata: None,
106                        filename: None,
107                        media_type: None,
108                    }],
109                    metadata: None,
110                    extensions: Vec::new(),
111                    reference_task_ids: Vec::new(),
112                }),
113                timestamp: Some("2026-03-12T12:00:00Z".to_owned()),
114            },
115            artifacts: Vec::new(),
116            history: Vec::new(),
117            metadata: None,
118        };
119
120        let json = serde_json::to_string(&task).expect("task should serialize");
121        let round_trip: Task = serde_json::from_str(&json).expect("task should deserialize");
122
123        assert_eq!(round_trip.id, "task-1");
124        assert_eq!(round_trip.context_id.as_deref(), Some("ctx-1"));
125        assert_eq!(round_trip.status.state, TaskState::Completed);
126    }
127}