Skip to main content

quantum_sdk/
missions.rs

1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4use crate::client::Client;
5use crate::error::Result;
6
7// ---------------------------------------------------------------------------
8// Request types
9// ---------------------------------------------------------------------------
10
11/// Request body for creating a mission.
12#[derive(Debug, Clone, Serialize, Default)]
13pub struct MissionCreateRequest {
14    /// High-level task description.
15    pub goal: String,
16
17    /// Strategy: "wave" (default), "dag", "mapreduce", "refinement", "branch".
18    #[serde(skip_serializing_if = "Option::is_none")]
19    pub strategy: Option<String>,
20
21    /// Conductor model (default: claude-sonnet-4-6).
22    #[serde(skip_serializing_if = "Option::is_none")]
23    pub conductor_model: Option<String>,
24
25    /// Conductor tier override. Default: "expensive".
26    /// Set to "cheap" when using a fast router as conductor.
27    #[serde(skip_serializing_if = "Option::is_none")]
28    pub conductor_tier: Option<String>,
29
30    /// Worker team configuration.
31    #[serde(skip_serializing_if = "Option::is_none")]
32    pub workers: Option<HashMap<String, MissionWorkerConfig>>,
33
34    /// Maximum orchestration steps (default: 25).
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub max_steps: Option<i32>,
37
38    /// Custom system prompt for the conductor.
39    #[serde(skip_serializing_if = "Option::is_none")]
40    pub system_prompt: Option<String>,
41
42    /// Existing session ID for context continuity.
43    #[serde(skip_serializing_if = "Option::is_none")]
44    pub session_id: Option<String>,
45}
46
47/// Worker configuration within a mission.
48#[derive(Debug, Clone, Serialize, Deserialize, Default)]
49pub struct MissionWorkerConfig {
50    /// Model to use for this worker.
51    pub model: String,
52
53    /// Cost tier: "cheap", "mid", "expensive".
54    #[serde(default)]
55    pub tier: String,
56
57    /// Worker description / capabilities.
58    #[serde(skip_serializing_if = "Option::is_none")]
59    pub description: Option<String>,
60
61    /// Worker to escalate to on failure (e.g. cheap coder → expensive coder).
62    #[serde(skip_serializing_if = "Option::is_none")]
63    pub escalate_to: Option<String>,
64
65    /// Max retries before escalating (default 1 = escalate on first failure).
66    #[serde(skip_serializing_if = "Option::is_none")]
67    pub max_retries: Option<i32>,
68}
69
70/// Request body for chatting with a mission's architect.
71#[derive(Debug, Clone, Serialize)]
72pub struct MissionChatRequest {
73    /// Message to send to the architect.
74    pub message: String,
75
76    /// Enable streaming (not yet supported).
77    #[serde(skip_serializing_if = "Option::is_none")]
78    pub stream: Option<bool>,
79}
80
81/// Request body for updating a mission plan.
82#[derive(Debug, Clone, Serialize, Default)]
83pub struct MissionPlanUpdate {
84    /// Updated task list.
85    #[serde(skip_serializing_if = "Option::is_none")]
86    pub tasks: Option<Vec<HashMap<String, serde_json::Value>>>,
87
88    /// Updated worker configuration.
89    #[serde(skip_serializing_if = "Option::is_none")]
90    pub workers: Option<HashMap<String, MissionWorkerConfig>>,
91
92    /// Additional system prompt.
93    #[serde(skip_serializing_if = "Option::is_none")]
94    pub system_prompt: Option<String>,
95
96    /// Updated max steps.
97    #[serde(skip_serializing_if = "Option::is_none")]
98    pub max_steps: Option<i32>,
99
100    /// Additional context to inject.
101    #[serde(skip_serializing_if = "Option::is_none")]
102    pub context: Option<String>,
103}
104
105/// Request body for confirming/rejecting a mission structure.
106#[derive(Debug, Clone, Serialize)]
107pub struct MissionConfirmStructure {
108    /// Whether the structure is approved.
109    pub confirmed: bool,
110
111    /// Rejection reason or modification notes.
112    #[serde(skip_serializing_if = "Option::is_none")]
113    pub feedback: Option<String>,
114}
115
116/// Request body for approving a completed mission.
117#[derive(Debug, Clone, Serialize, Default)]
118pub struct MissionApproveRequest {
119    /// Git commit SHA associated with the mission output.
120    #[serde(skip_serializing_if = "Option::is_none")]
121    pub commit_sha: Option<String>,
122
123    /// Approval comment.
124    #[serde(skip_serializing_if = "Option::is_none")]
125    pub comment: Option<String>,
126}
127
128/// Request body for importing a plan as a new mission.
129#[derive(Debug, Clone, Serialize, Default)]
130pub struct MissionImportRequest {
131    /// Mission goal.
132    pub goal: String,
133
134    /// Strategy.
135    #[serde(skip_serializing_if = "Option::is_none")]
136    pub strategy: Option<String>,
137
138    /// Conductor model.
139    #[serde(skip_serializing_if = "Option::is_none")]
140    pub conductor_model: Option<String>,
141
142    /// Worker configuration.
143    #[serde(skip_serializing_if = "Option::is_none")]
144    pub workers: Option<HashMap<String, MissionWorkerConfig>>,
145
146    /// Pre-defined tasks.
147    #[serde(default)]
148    pub tasks: Vec<HashMap<String, serde_json::Value>>,
149
150    /// System prompt.
151    #[serde(skip_serializing_if = "Option::is_none")]
152    pub system_prompt: Option<String>,
153
154    /// Maximum steps.
155    #[serde(skip_serializing_if = "Option::is_none")]
156    pub max_steps: Option<i32>,
157
158    /// Auto-execute after import.
159    #[serde(default)]
160    pub auto_execute: bool,
161}
162
163// ---------------------------------------------------------------------------
164// Response types
165// ---------------------------------------------------------------------------
166
167/// Response from mission creation.
168#[derive(Debug, Clone, Deserialize)]
169pub struct MissionCreateResponse {
170    /// Mission identifier.
171    pub mission_id: String,
172
173    /// Initial status.
174    #[serde(default)]
175    pub status: String,
176
177    /// Session ID for conversation context.
178    #[serde(default)]
179    pub session_id: Option<String>,
180
181    /// Conductor model used.
182    #[serde(default)]
183    pub conductor_model: Option<String>,
184
185    /// Strategy used.
186    #[serde(default)]
187    pub strategy: Option<String>,
188
189    /// Worker configuration.
190    #[serde(default)]
191    pub workers: Option<HashMap<String, MissionWorkerConfig>>,
192
193    /// Creation timestamp.
194    #[serde(default)]
195    pub created_at: Option<String>,
196
197    /// Request identifier.
198    #[serde(default)]
199    pub request_id: Option<String>,
200}
201
202/// Mission detail (from GET /missions/{id}).
203#[derive(Debug, Clone, Deserialize)]
204pub struct MissionDetail {
205    /// Mission identifier.
206    #[serde(default)]
207    pub id: Option<String>,
208
209    /// User who created the mission.
210    #[serde(default)]
211    pub user_id: Option<String>,
212
213    /// Mission goal.
214    #[serde(default)]
215    pub goal: Option<String>,
216
217    /// Strategy.
218    #[serde(default)]
219    pub strategy: Option<String>,
220
221    /// Conductor model.
222    #[serde(default)]
223    pub conductor_model: Option<String>,
224
225    /// Current status.
226    #[serde(default)]
227    pub status: Option<String>,
228
229    /// Creation timestamp.
230    #[serde(default)]
231    pub created_at: Option<String>,
232
233    /// Start timestamp.
234    #[serde(default)]
235    pub started_at: Option<String>,
236
237    /// Completion timestamp.
238    #[serde(default)]
239    pub completed_at: Option<String>,
240
241    /// Error message if failed.
242    #[serde(default)]
243    pub error: Option<String>,
244
245    /// Total cost in ticks.
246    #[serde(default)]
247    pub cost_ticks: i64,
248
249    /// Number of steps executed.
250    #[serde(default)]
251    pub total_steps: i32,
252
253    /// Session ID.
254    #[serde(default)]
255    pub session_id: Option<String>,
256
257    /// Final result text.
258    #[serde(default)]
259    pub result: Option<String>,
260
261    /// Tasks within the mission.
262    #[serde(default)]
263    pub tasks: Vec<MissionTask>,
264
265    /// Whether the mission was approved.
266    #[serde(default)]
267    pub approved: bool,
268
269    /// Commit SHA (if approved).
270    #[serde(default)]
271    pub commit_sha: Option<String>,
272}
273
274/// A task within a mission.
275#[derive(Debug, Clone, Deserialize, Default)]
276pub struct MissionTask {
277    /// Task identifier.
278    #[serde(default)]
279    pub id: Option<String>,
280
281    /// Task name.
282    #[serde(default)]
283    pub name: Option<String>,
284
285    /// Task description.
286    #[serde(default)]
287    pub description: Option<String>,
288
289    /// Assigned worker name.
290    #[serde(default)]
291    pub worker: Option<String>,
292
293    /// Model used.
294    #[serde(default)]
295    pub model: Option<String>,
296
297    /// Task status.
298    #[serde(default)]
299    pub status: Option<String>,
300
301    /// Task result.
302    #[serde(default)]
303    pub result: Option<String>,
304
305    /// Error message if failed.
306    #[serde(default)]
307    pub error: Option<String>,
308
309    /// Step number.
310    #[serde(default)]
311    pub step: i32,
312
313    /// Input tokens used.
314    #[serde(default)]
315    pub tokens_in: i32,
316
317    /// Output tokens used.
318    #[serde(default)]
319    pub tokens_out: i32,
320}
321
322/// Response from listing missions.
323#[derive(Debug, Clone, Deserialize)]
324pub struct MissionListResponse {
325    /// List of missions.
326    #[serde(default)]
327    pub missions: Vec<MissionDetail>,
328}
329
330/// Response from chatting with the architect.
331#[derive(Debug, Clone, Deserialize)]
332pub struct MissionChatResponse {
333    /// Mission identifier.
334    #[serde(default)]
335    pub mission_id: Option<String>,
336
337    /// Architect's response content.
338    #[serde(default)]
339    pub content: Option<String>,
340
341    /// Model used.
342    #[serde(default)]
343    pub model: Option<String>,
344
345    /// Cost in ticks.
346    #[serde(default)]
347    pub cost_ticks: i64,
348
349    /// Token usage.
350    #[serde(default)]
351    pub usage: Option<MissionChatUsage>,
352}
353
354/// Token usage for a mission chat response.
355#[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/// A git checkpoint within a mission.
364#[derive(Debug, Clone, Deserialize)]
365pub struct MissionCheckpoint {
366    /// Checkpoint identifier.
367    #[serde(default)]
368    pub id: Option<String>,
369
370    /// Commit SHA.
371    #[serde(default)]
372    pub commit_sha: Option<String>,
373
374    /// Checkpoint message.
375    #[serde(default)]
376    pub message: Option<String>,
377
378    /// Creation timestamp.
379    #[serde(default)]
380    pub created_at: Option<String>,
381}
382
383/// Response from listing checkpoints.
384#[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/// Generic status response for mission operations.
393#[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
411// ---------------------------------------------------------------------------
412// Client methods
413// ---------------------------------------------------------------------------
414
415impl Client {
416    /// Create and execute a mission asynchronously.
417    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    /// List missions for the authenticated user.
423    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    /// Get mission details including tasks.
433    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    /// Delete a mission.
439    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    /// Cancel a running mission.
445    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    /// Pause a running mission.
451    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    /// Resume a paused mission.
457    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    /// Chat with the mission's architect.
463    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    /// Retry a failed task.
469    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    /// Approve a completed mission.
475    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    /// Update the mission plan.
481    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    /// Confirm or reject the proposed execution structure.
487    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    /// List git checkpoints for a mission.
493    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    /// Import an existing plan as a new mission.
499    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}