1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
// Code generated by @uarp/codegen from spec/openapi.json. DO NOT EDIT.
//!
//! Agent-to-Agent protocol: discovery and task execution
#![allow(unused_imports, clippy::too_many_arguments)]
use reqwest::Method;
use serde::{Deserialize, Serialize};
use futures_core::Stream;
use crate::client::{Client, Request, NO_BODY, NO_QUERY};
use crate::error::Result;
use crate::generated::models;
use crate::multipart::{field_text, FilePart};
use crate::pagination::CursorGuard;
use crate::sse::EventStream;
use crate::util::encode_path;
/// Query and header parameters for `getAgentCard`.
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct GetAgentCardParams {
pub agent_id: String,
}
/// Query and header parameters for `listA2ATasks`.
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct ListA2ATasksParams {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub limit: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cursor: Option<String>,
}
/// Agent-to-Agent protocol: discovery and task execution
#[derive(Debug, Clone)]
pub struct A2AApi {
pub(crate) client: Client,
}
impl Client {
/// Agent-to-Agent protocol: discovery and task execution
pub fn a2a(&self) -> A2AApi {
A2AApi { client: self.clone() }
}
}
impl A2AApi {
/// A2A JSON-RPC 2.0 endpoint
///
/// Handles `tasks/send`, `tasks/sendSubscribe`, `tasks/get`, `tasks/cancel`,
/// `tasks/pushNotification/set`, `tasks/pushNotification/get` via JSON-RPC 2.0.
///
/// **Scope dispatch is dynamic per JSON-RPC method**: read-style methods (`tasks/get`,
/// `tasks/pushNotification/get`) require `agents:read`; write-style (`tasks/send`,
/// `tasks/sendSubscribe`, `tasks/cancel`, `tasks/pushNotification/set`) require `agents:write`.
/// The static `bearerAuth: \[agents:write\]` declared here is the *strictest* scope; an
/// `agents:read`-only key works for the read methods but the spec cannot express the per-method
/// conditional.
///
/// `POST /api/v1/a2a`
///
/// Required scopes: `agents:write`.
pub async fn a2a_json_rpc(&self, body: &models::A2ajsonRpcRequest) -> Result<serde_json::Value> {
self.client
.request_json(Request {
method: Method::POST,
path: "/api/v1/a2a".to_string(),
query: NO_QUERY,
body: Some(body),
headers: Vec::new(),
idempotent: true,
})
.await
}
/// Cancel an A2A task
///
/// `POST /api/v1/a2a/tasks/{taskId}/cancel`
///
/// Required scopes: `agents:write`.
pub async fn cancel_a2a_task(&self, task_id: &str) -> Result<serde_json::Value> {
self.client
.request_json(Request {
method: Method::POST,
path: format!("/api/v1/a2a/tasks/{}/cancel", encode_path(task_id)),
query: NO_QUERY,
body: NO_BODY,
headers: Vec::new(),
idempotent: true,
})
.await
}
/// Create an A2A task
///
/// Creates a new agent-to-agent task and schedules the underlying run.
///
/// `POST /api/v1/a2a/tasks`
///
/// Required scopes: `agents:write`.
pub async fn create_a2a_task(&self, body: &models::CreateA2ATaskRequest) -> Result<serde_json::Value> {
self.client
.request_json(Request {
method: Method::POST,
path: "/api/v1/a2a/tasks".to_string(),
query: NO_QUERY,
body: Some(body),
headers: Vec::new(),
idempotent: true,
})
.await
}
/// Get A2A task status
///
/// `GET /api/v1/a2a/tasks/{taskId}`
///
/// Required scopes: `agents:read`.
pub async fn get_a2a_task(&self, task_id: &str) -> Result<serde_json::Value> {
self.client
.request_json(Request {
method: Method::GET,
path: format!("/api/v1/a2a/tasks/{}", encode_path(task_id)),
query: NO_QUERY,
body: NO_BODY,
headers: Vec::new(),
idempotent: false,
})
.await
}
/// Get A2A agent card for discovery
///
/// `GET /.well-known/agent.json`
pub async fn get_agent_card(&self, params: &GetAgentCardParams) -> Result<serde_json::Value> {
self.client
.request_json(Request {
method: Method::GET,
path: "/.well-known/agent.json".to_string(),
query: Some(params),
body: NO_BODY,
headers: Vec::new(),
idempotent: false,
})
.await
}
/// List A2A tasks
///
/// `GET /api/v1/a2a/tasks`
///
/// Required scopes: `agents:read`.
pub async fn list_a2a_tasks(&self, params: &ListA2ATasksParams) -> Result<models::ListA2ATasksResponse> {
self.client
.request_json(Request {
method: Method::GET,
path: "/api/v1/a2a/tasks".to_string(),
query: Some(params),
body: NO_BODY,
headers: Vec::new(),
idempotent: false,
})
.await
}
/// Stream every item returned by `listA2ATasks`, following the `cursor` cursor until the server
/// reports no further pages.
pub fn list_a2a_tasks_all<'a>(&'a self, params: &'a ListA2ATasksParams) -> impl Stream<Item = Result<models::A2ATask>> + 'a {
async_stream::try_stream! {
let mut guard = CursorGuard::new();
let mut cursor = params.cursor.clone();
loop {
let mut page_params = params.clone();
page_params.cursor = cursor.clone();
let page = self.list_a2a_tasks(&page_params).await?;
let items = page.tasks;
let was_empty = items.is_empty();
for item in items {
yield item;
}
match guard.advance(page.cursor, page.has_more, was_empty) {
Some(next) => cursor = Some(next),
None => break,
}
}
}
}
/// Stream A2A task status updates (SSE)
///
/// `GET /api/v1/a2a/tasks/{taskId}/events`
///
/// Required scopes: `agents:read`.
///
/// Returns a server-sent event stream.
pub fn stream_a2a_task_events(&self, task_id: &str) -> EventStream {
self.client.request_stream(
&format!("/api/v1/a2a/tasks/{}/events", encode_path(task_id)),
NO_QUERY,
Vec::new(),
)
}
}