Skip to main content

tandem_workflows/
plan_package.rs

1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4// NOTE: This module is intentionally permissive and generic.
5// The host runtime (server, desktop, etc.) can specialize the type aliases to its
6// concrete schedule / step schema types without forcing this crate to depend on
7// host-only types.
8
9#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
10#[serde(rename_all = "snake_case")]
11pub enum AutomationV2ScheduleType {
12    Cron,
13    Interval,
14    Manual,
15}
16
17#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
18pub struct AutomationV2Schedule<MisfirePolicy = Value> {
19    #[serde(rename = "type")]
20    pub schedule_type: AutomationV2ScheduleType,
21    #[serde(default, skip_serializing_if = "Option::is_none")]
22    pub cron_expression: Option<String>,
23    #[serde(default, skip_serializing_if = "Option::is_none")]
24    pub interval_seconds: Option<u64>,
25    pub timezone: String,
26    pub misfire_policy: MisfirePolicy,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize, Default)]
30pub struct WorkflowPlanStep<InputRef = Value, OutputContract = Value> {
31    pub step_id: String,
32    pub kind: String,
33    pub objective: String,
34    #[serde(default)]
35    pub depends_on: Vec<String>,
36    pub agent_role: String,
37    #[serde(default)]
38    pub input_refs: Vec<InputRef>,
39    #[serde(default, skip_serializing_if = "Option::is_none")]
40    pub output_contract: Option<OutputContract>,
41    #[serde(default, skip_serializing_if = "Option::is_none")]
42    pub metadata: Option<Value>,
43}
44
45#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct WorkflowPlan<Schedule = Value, Step = Value> {
47    pub plan_id: String,
48    pub planner_version: String,
49    pub plan_source: String,
50    pub original_prompt: String,
51    pub normalized_prompt: String,
52    pub confidence: String,
53    pub title: String,
54    #[serde(default, skip_serializing_if = "Option::is_none")]
55    pub description: Option<String>,
56    pub schedule: Schedule,
57    pub execution_target: String,
58    pub workspace_root: String,
59    #[serde(default)]
60    pub steps: Vec<Step>,
61    #[serde(default)]
62    pub requires_integrations: Vec<String>,
63    #[serde(default)]
64    pub allowed_mcp_servers: Vec<String>,
65    #[serde(default, skip_serializing_if = "Option::is_none")]
66    pub operator_preferences: Option<Value>,
67    pub save_options: Value,
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct WorkflowPlanChatMessage {
72    pub role: String,
73    pub text: String,
74    pub created_at_ms: u64,
75}
76
77#[derive(Debug, Clone, Serialize, Deserialize)]
78pub struct WorkflowPlanConversation {
79    pub conversation_id: String,
80    pub plan_id: String,
81    pub created_at_ms: u64,
82    pub updated_at_ms: u64,
83    #[serde(default)]
84    pub messages: Vec<WorkflowPlanChatMessage>,
85}
86
87fn default_workflow_plan_draft_revision() -> u32 {
88    1
89}
90
91#[derive(Debug, Clone, Serialize, Deserialize)]
92pub struct WorkflowPlanDraftRecord<Plan = Value> {
93    pub initial_plan: Plan,
94    pub current_plan: Plan,
95    #[serde(default = "default_workflow_plan_draft_revision")]
96    pub plan_revision: u32,
97    pub conversation: WorkflowPlanConversation,
98    #[serde(default, skip_serializing_if = "Option::is_none")]
99    pub planner_diagnostics: Option<Value>,
100    #[serde(default, skip_serializing_if = "Option::is_none")]
101    pub last_success_materialization: Option<Value>,
102}