1use crate::routines::types::RoutineMisfirePolicy;
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4
5#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6#[serde(rename_all = "snake_case")]
7pub enum AutomationV2Status {
8 Active,
9 Paused,
10 Draft,
11}
12
13#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
14#[serde(rename_all = "snake_case")]
15pub enum AutomationV2ScheduleType {
16 Cron,
17 Interval,
18 Manual,
19}
20
21#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
22pub struct AutomationV2Schedule {
23 #[serde(rename = "type")]
24 pub schedule_type: AutomationV2ScheduleType,
25 #[serde(default, skip_serializing_if = "Option::is_none")]
26 pub cron_expression: Option<String>,
27 #[serde(default, skip_serializing_if = "Option::is_none")]
28 pub interval_seconds: Option<u64>,
29 pub timezone: String,
30 pub misfire_policy: RoutineMisfirePolicy,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct AutomationAgentToolPolicy {
35 #[serde(default)]
36 pub allowlist: Vec<String>,
37 #[serde(default)]
38 pub denylist: Vec<String>,
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct AutomationAgentMcpPolicy {
43 #[serde(default)]
44 pub allowed_servers: Vec<String>,
45 #[serde(default, skip_serializing_if = "Option::is_none")]
46 pub allowed_tools: Option<Vec<String>>,
47}
48
49#[derive(Debug, Clone, Serialize, Deserialize)]
50pub struct AutomationAgentProfile {
51 pub agent_id: String,
52 #[serde(default, skip_serializing_if = "Option::is_none")]
53 pub template_id: Option<String>,
54 pub display_name: String,
55 #[serde(default, skip_serializing_if = "Option::is_none")]
56 pub avatar_url: Option<String>,
57 #[serde(default, skip_serializing_if = "Option::is_none")]
58 pub model_policy: Option<Value>,
59 #[serde(default)]
60 pub skills: Vec<String>,
61 pub tool_policy: AutomationAgentToolPolicy,
62 pub mcp_policy: AutomationAgentMcpPolicy,
63 #[serde(default, skip_serializing_if = "Option::is_none")]
64 pub approval_policy: Option<String>,
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
68#[serde(rename_all = "snake_case")]
69pub enum AutomationNodeStageKind {
70 Orchestrator,
71 Workstream,
72 Review,
73 Test,
74 Approval,
75}
76
77#[derive(Debug, Clone, Serialize, Deserialize)]
78pub struct AutomationApprovalGate {
79 #[serde(default)]
80 pub required: bool,
81 #[serde(default)]
82 pub decisions: Vec<String>,
83 #[serde(default)]
84 pub rework_targets: Vec<String>,
85 #[serde(default, skip_serializing_if = "Option::is_none")]
86 pub instructions: Option<String>,
87}
88
89#[derive(Debug, Clone, Serialize, Deserialize)]
90pub struct AutomationFlowNode {
91 pub node_id: String,
92 pub agent_id: String,
93 pub objective: String,
94 #[serde(default)]
95 pub depends_on: Vec<String>,
96 #[serde(default)]
97 pub input_refs: Vec<AutomationFlowInputRef>,
98 #[serde(default, skip_serializing_if = "Option::is_none")]
99 pub output_contract: Option<AutomationFlowOutputContract>,
100 #[serde(default, skip_serializing_if = "Option::is_none")]
101 pub retry_policy: Option<Value>,
102 #[serde(default, skip_serializing_if = "Option::is_none")]
103 pub timeout_ms: Option<u64>,
104 #[serde(default, skip_serializing_if = "Option::is_none")]
105 pub stage_kind: Option<AutomationNodeStageKind>,
106 #[serde(default, skip_serializing_if = "Option::is_none")]
107 pub gate: Option<AutomationApprovalGate>,
108 #[serde(default, skip_serializing_if = "Option::is_none")]
109 pub metadata: Option<Value>,
110}
111
112#[derive(Debug, Clone, Serialize, Deserialize)]
113pub struct AutomationFlowInputRef {
114 pub from_step_id: String,
115 pub alias: String,
116}
117
118#[derive(Debug, Clone, Serialize, Deserialize)]
119pub struct AutomationFlowOutputContract {
120 pub kind: String,
121 #[serde(default, skip_serializing_if = "Option::is_none")]
122 pub validator: Option<AutomationOutputValidatorKind>,
123 #[serde(default, skip_serializing_if = "Option::is_none")]
124 pub enforcement: Option<AutomationOutputEnforcement>,
125 #[serde(default, skip_serializing_if = "Option::is_none")]
126 pub schema: Option<Value>,
127 #[serde(default, skip_serializing_if = "Option::is_none")]
128 pub summary_guidance: Option<String>,
129}
130
131#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
132pub struct AutomationOutputEnforcement {
133 #[serde(default, skip_serializing_if = "Option::is_none")]
134 pub validation_profile: Option<String>,
135 #[serde(default)]
136 pub required_tools: Vec<String>,
137 #[serde(default)]
138 pub required_evidence: Vec<String>,
139 #[serde(default)]
140 pub required_sections: Vec<String>,
141 #[serde(default)]
142 pub prewrite_gates: Vec<String>,
143 #[serde(default)]
144 pub retry_on_missing: Vec<String>,
145 #[serde(default)]
146 pub terminal_on: Vec<String>,
147 #[serde(default, skip_serializing_if = "Option::is_none")]
148 pub repair_budget: Option<u32>,
149 #[serde(default, skip_serializing_if = "Option::is_none")]
150 pub session_text_recovery: Option<String>,
151}
152
153#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
154#[serde(rename_all = "snake_case")]
155pub enum AutomationOutputValidatorKind {
156 CodePatch,
157 ResearchBrief,
158 ReviewDecision,
159 StructuredJson,
160 GenericArtifact,
161}
162
163#[derive(Debug, Clone, Serialize, Deserialize)]
164pub struct AutomationFlowSpec {
165 #[serde(default)]
166 pub nodes: Vec<AutomationFlowNode>,
167}
168
169#[derive(Debug, Clone, Serialize, Deserialize)]
170pub struct AutomationExecutionPolicy {
171 #[serde(default, skip_serializing_if = "Option::is_none")]
172 pub max_parallel_agents: Option<u32>,
173 #[serde(default, skip_serializing_if = "Option::is_none")]
174 pub max_total_runtime_ms: Option<u64>,
175 #[serde(default, skip_serializing_if = "Option::is_none")]
176 pub max_total_tool_calls: Option<u32>,
177 #[serde(default, skip_serializing_if = "Option::is_none")]
178 pub max_total_tokens: Option<u64>,
179 #[serde(default, skip_serializing_if = "Option::is_none")]
180 pub max_total_cost_usd: Option<f64>,
181}
182
183#[derive(Debug, Clone, Serialize, Deserialize)]
184pub struct AutomationV2Spec {
185 pub automation_id: String,
186 pub name: String,
187 #[serde(default, skip_serializing_if = "Option::is_none")]
188 pub description: Option<String>,
189 pub status: AutomationV2Status,
190 pub schedule: AutomationV2Schedule,
191 #[serde(default)]
192 pub agents: Vec<AutomationAgentProfile>,
193 pub flow: AutomationFlowSpec,
194 pub execution: AutomationExecutionPolicy,
195 #[serde(default)]
196 pub output_targets: Vec<String>,
197 pub created_at_ms: u64,
198 pub updated_at_ms: u64,
199 pub creator_id: String,
200 #[serde(default, skip_serializing_if = "Option::is_none")]
201 pub workspace_root: Option<String>,
202 #[serde(default, skip_serializing_if = "Option::is_none")]
203 pub metadata: Option<Value>,
204 #[serde(default, skip_serializing_if = "Option::is_none")]
205 pub next_fire_at_ms: Option<u64>,
206 #[serde(default, skip_serializing_if = "Option::is_none")]
207 pub last_fired_at_ms: Option<u64>,
208}
209
210#[derive(Debug, Clone, Serialize, Deserialize)]
211pub struct WorkflowPlanStep {
212 pub step_id: String,
213 pub kind: String,
214 pub objective: String,
215 #[serde(default)]
216 pub depends_on: Vec<String>,
217 pub agent_role: String,
218 #[serde(default)]
219 pub input_refs: Vec<AutomationFlowInputRef>,
220 #[serde(default, skip_serializing_if = "Option::is_none")]
221 pub output_contract: Option<AutomationFlowOutputContract>,
222 #[serde(default, skip_serializing_if = "Option::is_none")]
223 pub metadata: Option<Value>,
224}
225
226#[derive(Debug, Clone, Serialize, Deserialize)]
227pub struct WorkflowPlan {
228 pub plan_id: String,
229 pub planner_version: String,
230 pub plan_source: String,
231 pub original_prompt: String,
232 pub normalized_prompt: String,
233 pub confidence: String,
234 pub title: String,
235 #[serde(default, skip_serializing_if = "Option::is_none")]
236 pub description: Option<String>,
237 pub schedule: AutomationV2Schedule,
238 pub execution_target: String,
239 pub workspace_root: String,
240 #[serde(default)]
241 pub steps: Vec<WorkflowPlanStep>,
242 #[serde(default)]
243 pub requires_integrations: Vec<String>,
244 #[serde(default)]
245 pub allowed_mcp_servers: Vec<String>,
246 #[serde(default, skip_serializing_if = "Option::is_none")]
247 pub operator_preferences: Option<Value>,
248 pub save_options: Value,
249}
250
251#[derive(Debug, Clone, Serialize, Deserialize)]
252pub struct WorkflowPlanChatMessage {
253 pub role: String,
254 pub text: String,
255 pub created_at_ms: u64,
256}
257
258#[derive(Debug, Clone, Serialize, Deserialize)]
259pub struct WorkflowPlanConversation {
260 pub conversation_id: String,
261 pub plan_id: String,
262 pub created_at_ms: u64,
263 pub updated_at_ms: u64,
264 #[serde(default)]
265 pub messages: Vec<WorkflowPlanChatMessage>,
266}
267
268#[derive(Debug, Clone, Serialize, Deserialize)]
269pub struct WorkflowPlanDraftRecord {
270 pub initial_plan: WorkflowPlan,
271 pub current_plan: WorkflowPlan,
272 pub conversation: WorkflowPlanConversation,
273 #[serde(default, skip_serializing_if = "Option::is_none")]
274 pub planner_diagnostics: Option<Value>,
275}
276
277#[derive(Debug, Clone, Serialize, Deserialize)]
278pub struct AutomationNodeOutput {
279 pub contract_kind: String,
280 #[serde(default, skip_serializing_if = "Option::is_none")]
281 pub validator_kind: Option<AutomationOutputValidatorKind>,
282 #[serde(default, skip_serializing_if = "Option::is_none")]
283 pub validator_summary: Option<AutomationValidatorSummary>,
284 pub summary: String,
285 pub content: Value,
286 pub created_at_ms: u64,
287 pub node_id: String,
288 #[serde(default, skip_serializing_if = "Option::is_none")]
289 pub status: Option<String>,
290 #[serde(default, skip_serializing_if = "Option::is_none")]
291 pub blocked_reason: Option<String>,
292 #[serde(default, skip_serializing_if = "Option::is_none")]
293 pub approved: Option<bool>,
294 #[serde(default, skip_serializing_if = "Option::is_none")]
295 pub workflow_class: Option<String>,
296 #[serde(default, skip_serializing_if = "Option::is_none")]
297 pub phase: Option<String>,
298 #[serde(default, skip_serializing_if = "Option::is_none")]
299 pub failure_kind: Option<String>,
300 #[serde(default, skip_serializing_if = "Option::is_none")]
301 pub tool_telemetry: Option<Value>,
302 #[serde(default, skip_serializing_if = "Option::is_none")]
303 pub preflight: Option<Value>,
304 #[serde(default, skip_serializing_if = "Option::is_none")]
305 pub capability_resolution: Option<Value>,
306 #[serde(default, skip_serializing_if = "Option::is_none")]
307 pub attempt_evidence: Option<Value>,
308 #[serde(default, skip_serializing_if = "Option::is_none")]
309 pub blocker_category: Option<String>,
310 #[serde(default, skip_serializing_if = "Option::is_none")]
311 pub fallback_used: Option<bool>,
312 #[serde(default, skip_serializing_if = "Option::is_none")]
313 pub artifact_validation: Option<Value>,
314}
315
316#[derive(Debug, Clone, Serialize, Deserialize)]
317pub struct AutomationValidatorSummary {
318 pub kind: AutomationOutputValidatorKind,
319 pub outcome: String,
320 #[serde(default, skip_serializing_if = "Option::is_none")]
321 pub reason: Option<String>,
322 #[serde(default)]
323 pub unmet_requirements: Vec<String>,
324 #[serde(default)]
325 pub warning_requirements: Vec<String>,
326 #[serde(default)]
327 pub warning_count: u32,
328 #[serde(default, skip_serializing_if = "Option::is_none")]
329 pub accepted_candidate_source: Option<String>,
330 #[serde(default, skip_serializing_if = "Option::is_none")]
331 pub verification_outcome: Option<String>,
332 #[serde(default)]
333 pub repair_attempted: bool,
334 #[serde(default)]
335 pub repair_attempt: u32,
336 #[serde(default)]
337 pub repair_attempts_remaining: u32,
338 #[serde(default)]
339 pub repair_succeeded: bool,
340 #[serde(default)]
341 pub repair_exhausted: bool,
342}
343
344#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
345#[serde(rename_all = "snake_case")]
346pub enum AutomationRunStatus {
347 Queued,
348 Running,
349 Pausing,
350 Paused,
351 AwaitingApproval,
352 Completed,
353 Blocked,
354 Failed,
355 Cancelled,
356}
357
358#[derive(Debug, Clone, Serialize, Deserialize)]
359pub struct AutomationPendingGate {
360 pub node_id: String,
361 pub title: String,
362 #[serde(default, skip_serializing_if = "Option::is_none")]
363 pub instructions: Option<String>,
364 #[serde(default)]
365 pub decisions: Vec<String>,
366 #[serde(default)]
367 pub rework_targets: Vec<String>,
368 pub requested_at_ms: u64,
369 #[serde(default)]
370 pub upstream_node_ids: Vec<String>,
371}
372
373#[derive(Debug, Clone, Serialize, Deserialize)]
374pub struct AutomationGateDecisionRecord {
375 pub node_id: String,
376 pub decision: String,
377 #[serde(default, skip_serializing_if = "Option::is_none")]
378 pub reason: Option<String>,
379 pub decided_at_ms: u64,
380}
381
382#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
383#[serde(rename_all = "snake_case")]
384pub enum AutomationStopKind {
385 Cancelled,
386 OperatorStopped,
387 GuardrailStopped,
388}
389
390#[derive(Debug, Clone, Serialize, Deserialize)]
391pub struct AutomationLifecycleRecord {
392 pub event: String,
393 pub recorded_at_ms: u64,
394 #[serde(default, skip_serializing_if = "Option::is_none")]
395 pub reason: Option<String>,
396 #[serde(default, skip_serializing_if = "Option::is_none")]
397 pub stop_kind: Option<AutomationStopKind>,
398 #[serde(default, skip_serializing_if = "Option::is_none")]
399 pub metadata: Option<Value>,
400}
401
402#[derive(Debug, Clone, Serialize, Deserialize)]
403pub struct AutomationFailureRecord {
404 pub node_id: String,
405 pub reason: String,
406 pub failed_at_ms: u64,
407}
408
409#[derive(Debug, Clone, Serialize, Deserialize)]
410pub struct AutomationRunCheckpoint {
411 #[serde(default)]
412 pub completed_nodes: Vec<String>,
413 #[serde(default)]
414 pub pending_nodes: Vec<String>,
415 #[serde(default)]
416 pub node_outputs: std::collections::HashMap<String, Value>,
417 #[serde(default)]
418 pub node_attempts: std::collections::HashMap<String, u32>,
419 #[serde(default)]
420 pub blocked_nodes: Vec<String>,
421 #[serde(default, skip_serializing_if = "Option::is_none")]
422 pub awaiting_gate: Option<AutomationPendingGate>,
423 #[serde(default)]
424 pub gate_history: Vec<AutomationGateDecisionRecord>,
425 #[serde(default)]
426 pub lifecycle_history: Vec<AutomationLifecycleRecord>,
427 #[serde(default, skip_serializing_if = "Option::is_none")]
428 pub last_failure: Option<AutomationFailureRecord>,
429}
430
431#[derive(Debug, Clone, Serialize, Deserialize)]
432pub struct AutomationV2RunRecord {
433 pub run_id: String,
434 pub automation_id: String,
435 pub trigger_type: String,
436 pub status: AutomationRunStatus,
437 pub created_at_ms: u64,
438 pub updated_at_ms: u64,
439 #[serde(default, skip_serializing_if = "Option::is_none")]
440 pub started_at_ms: Option<u64>,
441 #[serde(default, skip_serializing_if = "Option::is_none")]
442 pub finished_at_ms: Option<u64>,
443 #[serde(default)]
444 pub active_session_ids: Vec<String>,
445 #[serde(default, skip_serializing_if = "Option::is_none")]
446 pub latest_session_id: Option<String>,
447 #[serde(default)]
448 pub active_instance_ids: Vec<String>,
449 pub checkpoint: AutomationRunCheckpoint,
450 #[serde(default, skip_serializing_if = "Option::is_none")]
451 pub automation_snapshot: Option<AutomationV2Spec>,
452 #[serde(default, skip_serializing_if = "Option::is_none")]
453 pub pause_reason: Option<String>,
454 #[serde(default, skip_serializing_if = "Option::is_none")]
455 pub resume_reason: Option<String>,
456 #[serde(default, skip_serializing_if = "Option::is_none")]
457 pub detail: Option<String>,
458 #[serde(default, skip_serializing_if = "Option::is_none")]
459 pub stop_kind: Option<AutomationStopKind>,
460 #[serde(default, skip_serializing_if = "Option::is_none")]
461 pub stop_reason: Option<String>,
462 #[serde(default)]
463 pub prompt_tokens: u64,
464 #[serde(default)]
465 pub completion_tokens: u64,
466 #[serde(default)]
467 pub total_tokens: u64,
468 #[serde(default)]
469 pub estimated_cost_usd: f64,
470}