1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4use crate::client::Client;
5use crate::error::Result;
6
7#[derive(Debug, Clone, Serialize, Default)]
13pub struct MissionCreateRequest {
14 pub goal: String,
16
17 #[serde(skip_serializing_if = "Option::is_none")]
19 pub strategy: Option<String>,
20
21 #[serde(skip_serializing_if = "Option::is_none")]
23 pub conductor_model: Option<String>,
24
25 #[serde(skip_serializing_if = "Option::is_none")]
28 pub conductor_tier: Option<String>,
29
30 #[serde(skip_serializing_if = "Option::is_none")]
32 pub workers: Option<HashMap<String, MissionWorkerConfig>>,
33
34 #[serde(skip_serializing_if = "Option::is_none")]
36 pub max_steps: Option<i32>,
37
38 #[serde(skip_serializing_if = "Option::is_none")]
40 pub system_prompt: Option<String>,
41
42 #[serde(skip_serializing_if = "Option::is_none")]
44 pub session_id: Option<String>,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize, Default)]
49pub struct MissionWorkerConfig {
50 pub model: String,
52
53 #[serde(default)]
55 pub tier: String,
56
57 #[serde(skip_serializing_if = "Option::is_none")]
59 pub description: Option<String>,
60
61 #[serde(skip_serializing_if = "Option::is_none")]
63 pub escalate_to: Option<String>,
64
65 #[serde(skip_serializing_if = "Option::is_none")]
67 pub max_retries: Option<i32>,
68}
69
70#[derive(Debug, Clone, Serialize)]
72pub struct MissionChatRequest {
73 pub message: String,
75
76 #[serde(skip_serializing_if = "Option::is_none")]
78 pub stream: Option<bool>,
79}
80
81#[derive(Debug, Clone, Serialize, Default)]
83pub struct MissionPlanUpdate {
84 #[serde(skip_serializing_if = "Option::is_none")]
86 pub tasks: Option<Vec<HashMap<String, serde_json::Value>>>,
87
88 #[serde(skip_serializing_if = "Option::is_none")]
90 pub workers: Option<HashMap<String, MissionWorkerConfig>>,
91
92 #[serde(skip_serializing_if = "Option::is_none")]
94 pub system_prompt: Option<String>,
95
96 #[serde(skip_serializing_if = "Option::is_none")]
98 pub max_steps: Option<i32>,
99
100 #[serde(skip_serializing_if = "Option::is_none")]
102 pub context: Option<String>,
103}
104
105#[derive(Debug, Clone, Serialize)]
107pub struct MissionConfirmStructure {
108 pub confirmed: bool,
110
111 #[serde(skip_serializing_if = "Option::is_none")]
113 pub feedback: Option<String>,
114}
115
116#[derive(Debug, Clone, Serialize, Default)]
118pub struct MissionApproveRequest {
119 #[serde(skip_serializing_if = "Option::is_none")]
121 pub commit_sha: Option<String>,
122
123 #[serde(skip_serializing_if = "Option::is_none")]
125 pub comment: Option<String>,
126}
127
128#[derive(Debug, Clone, Serialize, Default)]
130pub struct MissionImportRequest {
131 pub goal: String,
133
134 #[serde(skip_serializing_if = "Option::is_none")]
136 pub strategy: Option<String>,
137
138 #[serde(skip_serializing_if = "Option::is_none")]
140 pub conductor_model: Option<String>,
141
142 #[serde(skip_serializing_if = "Option::is_none")]
144 pub workers: Option<HashMap<String, MissionWorkerConfig>>,
145
146 #[serde(default)]
148 pub tasks: Vec<HashMap<String, serde_json::Value>>,
149
150 #[serde(skip_serializing_if = "Option::is_none")]
152 pub system_prompt: Option<String>,
153
154 #[serde(skip_serializing_if = "Option::is_none")]
156 pub max_steps: Option<i32>,
157
158 #[serde(default)]
160 pub auto_execute: bool,
161}
162
163#[derive(Debug, Clone, Deserialize)]
169pub struct MissionCreateResponse {
170 pub mission_id: String,
172
173 #[serde(default)]
175 pub status: String,
176
177 #[serde(default)]
179 pub session_id: Option<String>,
180
181 #[serde(default)]
183 pub conductor_model: Option<String>,
184
185 #[serde(default)]
187 pub strategy: Option<String>,
188
189 #[serde(default)]
191 pub workers: Option<HashMap<String, MissionWorkerConfig>>,
192
193 #[serde(default)]
195 pub created_at: Option<String>,
196
197 #[serde(default)]
199 pub request_id: Option<String>,
200}
201
202#[derive(Debug, Clone, Deserialize)]
204pub struct MissionDetail {
205 #[serde(default)]
207 pub id: Option<String>,
208
209 #[serde(default)]
211 pub user_id: Option<String>,
212
213 #[serde(default)]
215 pub goal: Option<String>,
216
217 #[serde(default)]
219 pub strategy: Option<String>,
220
221 #[serde(default)]
223 pub conductor_model: Option<String>,
224
225 #[serde(default)]
227 pub status: Option<String>,
228
229 #[serde(default)]
231 pub created_at: Option<String>,
232
233 #[serde(default)]
235 pub started_at: Option<String>,
236
237 #[serde(default)]
239 pub completed_at: Option<String>,
240
241 #[serde(default)]
243 pub error: Option<String>,
244
245 #[serde(default)]
247 pub cost_ticks: i64,
248
249 #[serde(default)]
251 pub total_steps: i32,
252
253 #[serde(default)]
255 pub session_id: Option<String>,
256
257 #[serde(default)]
259 pub result: Option<String>,
260
261 #[serde(default)]
263 pub tasks: Vec<MissionTask>,
264
265 #[serde(default)]
267 pub approved: bool,
268
269 #[serde(default)]
271 pub commit_sha: Option<String>,
272}
273
274#[derive(Debug, Clone, Deserialize, Default)]
276pub struct MissionTask {
277 #[serde(default)]
279 pub id: Option<String>,
280
281 #[serde(default)]
283 pub name: Option<String>,
284
285 #[serde(default)]
287 pub description: Option<String>,
288
289 #[serde(default)]
291 pub worker: Option<String>,
292
293 #[serde(default)]
295 pub model: Option<String>,
296
297 #[serde(default)]
299 pub status: Option<String>,
300
301 #[serde(default)]
303 pub result: Option<String>,
304
305 #[serde(default)]
307 pub error: Option<String>,
308
309 #[serde(default)]
311 pub step: i32,
312
313 #[serde(default)]
315 pub tokens_in: i32,
316
317 #[serde(default)]
319 pub tokens_out: i32,
320}
321
322#[derive(Debug, Clone, Deserialize)]
324pub struct MissionListResponse {
325 #[serde(default)]
327 pub missions: Vec<MissionDetail>,
328}
329
330#[derive(Debug, Clone, Deserialize)]
332pub struct MissionChatResponse {
333 #[serde(default)]
335 pub mission_id: Option<String>,
336
337 #[serde(default)]
339 pub content: Option<String>,
340
341 #[serde(default)]
343 pub model: Option<String>,
344
345 #[serde(default)]
347 pub cost_ticks: i64,
348
349 #[serde(default)]
351 pub usage: Option<MissionChatUsage>,
352}
353
354#[derive(Debug, Clone, Deserialize, Default)]
356pub struct MissionChatUsage {
357 #[serde(default)]
358 pub input_tokens: i32,
359 #[serde(default)]
360 pub output_tokens: i32,
361}
362
363#[derive(Debug, Clone, Deserialize)]
365pub struct MissionCheckpoint {
366 #[serde(default)]
368 pub id: Option<String>,
369
370 #[serde(default)]
372 pub commit_sha: Option<String>,
373
374 #[serde(default)]
376 pub message: Option<String>,
377
378 #[serde(default)]
380 pub created_at: Option<String>,
381}
382
383#[derive(Debug, Clone, Deserialize)]
385pub struct MissionCheckpointsResponse {
386 #[serde(default)]
387 pub mission_id: Option<String>,
388 #[serde(default)]
389 pub checkpoints: Vec<MissionCheckpoint>,
390}
391
392#[derive(Debug, Clone, Deserialize)]
394pub struct MissionStatusResponse {
395 #[serde(default)]
396 pub mission_id: Option<String>,
397 #[serde(default)]
398 pub status: Option<String>,
399 #[serde(default)]
400 pub confirmed: Option<bool>,
401 #[serde(default)]
402 pub approved: Option<bool>,
403 #[serde(default)]
404 pub deleted: Option<bool>,
405 #[serde(default)]
406 pub updated: Option<bool>,
407 #[serde(default)]
408 pub commit_sha: Option<String>,
409}
410
411impl Client {
416 pub async fn mission_create(&self, req: &MissionCreateRequest) -> Result<MissionCreateResponse> {
418 let (resp, _) = self.post_json("/qai/v1/missions/create", req).await?;
419 Ok(resp)
420 }
421
422 pub async fn mission_list(&self, status: Option<&str>) -> Result<MissionListResponse> {
424 let path = match status {
425 Some(s) => format!("/qai/v1/missions/list?status={}", s),
426 None => "/qai/v1/missions/list".into(),
427 };
428 let (resp, _) = self.get_json(&path).await?;
429 Ok(resp)
430 }
431
432 pub async fn mission_get(&self, mission_id: &str) -> Result<MissionDetail> {
434 let (resp, _) = self.get_json(&format!("/qai/v1/missions/{}", mission_id)).await?;
435 Ok(resp)
436 }
437
438 pub async fn mission_delete(&self, mission_id: &str) -> Result<MissionStatusResponse> {
440 let (resp, _) = self.delete_json(&format!("/qai/v1/missions/{}", mission_id)).await?;
441 Ok(resp)
442 }
443
444 pub async fn mission_cancel(&self, mission_id: &str) -> Result<MissionStatusResponse> {
446 let (resp, _) = self.post_json_empty(&format!("/qai/v1/missions/{}/cancel", mission_id)).await?;
447 Ok(resp)
448 }
449
450 pub async fn mission_pause(&self, mission_id: &str) -> Result<MissionStatusResponse> {
452 let (resp, _) = self.post_json_empty(&format!("/qai/v1/missions/{}/pause", mission_id)).await?;
453 Ok(resp)
454 }
455
456 pub async fn mission_resume(&self, mission_id: &str) -> Result<MissionStatusResponse> {
458 let (resp, _) = self.post_json_empty(&format!("/qai/v1/missions/{}/resume", mission_id)).await?;
459 Ok(resp)
460 }
461
462 pub async fn mission_chat(&self, mission_id: &str, req: &MissionChatRequest) -> Result<MissionChatResponse> {
464 let (resp, _) = self.post_json(&format!("/qai/v1/missions/{}/chat", mission_id), req).await?;
465 Ok(resp)
466 }
467
468 pub async fn mission_retry_task(&self, mission_id: &str, task_id: &str) -> Result<MissionStatusResponse> {
470 let (resp, _) = self.post_json_empty(&format!("/qai/v1/missions/{}/retry/{}", mission_id, task_id)).await?;
471 Ok(resp)
472 }
473
474 pub async fn mission_approve(&self, mission_id: &str, req: &MissionApproveRequest) -> Result<MissionStatusResponse> {
476 let (resp, _) = self.post_json(&format!("/qai/v1/missions/{}/approve", mission_id), req).await?;
477 Ok(resp)
478 }
479
480 pub async fn mission_update_plan(&self, mission_id: &str, req: &MissionPlanUpdate) -> Result<MissionStatusResponse> {
482 let (resp, _) = self.put_json(&format!("/qai/v1/missions/{}/plan", mission_id), req).await?;
483 Ok(resp)
484 }
485
486 pub async fn mission_confirm_structure(&self, mission_id: &str, req: &MissionConfirmStructure) -> Result<MissionStatusResponse> {
488 let (resp, _) = self.post_json(&format!("/qai/v1/missions/{}/confirm-structure", mission_id), req).await?;
489 Ok(resp)
490 }
491
492 pub async fn mission_checkpoints(&self, mission_id: &str) -> Result<MissionCheckpointsResponse> {
494 let (resp, _) = self.get_json(&format!("/qai/v1/missions/{}/checkpoints", mission_id)).await?;
495 Ok(resp)
496 }
497
498 pub async fn mission_import(&self, req: &MissionImportRequest) -> Result<MissionCreateResponse> {
500 let (resp, _) = self.post_json("/qai/v1/missions/import", req).await?;
501 Ok(resp)
502 }
503}