Skip to main content

roder_protocol/
workflows.rs

1use roder_api::dynamic_workflows::{
2    WorkflowAgentRun, WorkflowAgentRunId, WorkflowApproval, WorkflowApprovalDecision, WorkflowRun,
3    WorkflowRunId, WorkflowRunSummary, WorkflowScript, WorkflowScriptId, WorkflowScriptSourceKind,
4};
5use roder_api::events::{ThreadId, TurnId};
6use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
9#[serde(rename_all = "camelCase")]
10pub struct WorkflowsPlanParams {
11    #[serde(default, skip_serializing_if = "Option::is_none")]
12    pub thread_id: Option<ThreadId>,
13    #[serde(default, skip_serializing_if = "Option::is_none")]
14    pub turn_id: Option<TurnId>,
15    pub prompt: String,
16    #[serde(default, skip_serializing_if = "Option::is_none")]
17    pub workspace: Option<String>,
18    #[serde(default)]
19    pub arguments: serde_json::Value,
20    #[serde(default, skip_serializing_if = "Option::is_none")]
21    pub script: Option<String>,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
25#[serde(rename_all = "camelCase")]
26pub struct WorkflowsPlanResult {
27    pub run: WorkflowRun,
28    pub approval_required: bool,
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
32#[serde(rename_all = "camelCase")]
33pub struct WorkflowsApproveParams {
34    pub run_id: WorkflowRunId,
35    pub decision: WorkflowApprovalDecision,
36    #[serde(default, skip_serializing_if = "Option::is_none")]
37    pub reason: Option<String>,
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
41#[serde(rename_all = "camelCase")]
42pub struct WorkflowsApproveResult {
43    pub run: WorkflowRun,
44    pub approval: WorkflowApproval,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
48#[serde(rename_all = "camelCase")]
49pub struct WorkflowsListParams {
50    #[serde(default, skip_serializing_if = "Option::is_none")]
51    pub thread_id: Option<ThreadId>,
52    #[serde(default)]
53    pub include_terminal: bool,
54}
55
56#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
57#[serde(rename_all = "camelCase")]
58pub struct WorkflowsListResult {
59    pub runs: Vec<WorkflowRunSummary>,
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
63#[serde(rename_all = "camelCase")]
64pub struct WorkflowsGetParams {
65    pub run_id: WorkflowRunId,
66    #[serde(default)]
67    pub include_script_body: bool,
68    #[serde(default)]
69    pub include_agents: bool,
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
73#[serde(rename_all = "camelCase")]
74pub struct WorkflowsGetResult {
75    pub run: WorkflowRun,
76}
77
78#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
79#[serde(rename_all = "camelCase")]
80pub struct WorkflowsPauseParams {
81    pub run_id: WorkflowRunId,
82    #[serde(default)]
83    pub cancel_running_agents: bool,
84    #[serde(default, skip_serializing_if = "Option::is_none")]
85    pub reason: Option<String>,
86}
87
88#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
89#[serde(rename_all = "camelCase")]
90pub struct WorkflowsPauseResult {
91    pub run: WorkflowRun,
92}
93
94#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
95#[serde(rename_all = "camelCase")]
96pub struct WorkflowsResumeParams {
97    pub run_id: WorkflowRunId,
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
101#[serde(rename_all = "camelCase")]
102pub struct WorkflowsResumeResult {
103    pub run: WorkflowRun,
104}
105
106#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
107#[serde(rename_all = "camelCase")]
108pub struct WorkflowsStopParams {
109    pub run_id: WorkflowRunId,
110    #[serde(default, skip_serializing_if = "Option::is_none")]
111    pub reason: Option<String>,
112}
113
114#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
115#[serde(rename_all = "camelCase")]
116pub struct WorkflowsStopResult {
117    pub run: WorkflowRun,
118}
119
120#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
121#[serde(rename_all = "camelCase")]
122pub struct WorkflowsRestartAgentParams {
123    pub run_id: WorkflowRunId,
124    pub agent_id: WorkflowAgentRunId,
125}
126
127#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
128#[serde(rename_all = "camelCase")]
129pub struct WorkflowsRestartAgentResult {
130    pub run: WorkflowRun,
131    pub agent: WorkflowAgentRun,
132}
133
134#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
135#[serde(rename_all = "camelCase")]
136pub enum WorkflowsSaveScope {
137    User,
138    Workspace,
139}
140
141#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
142#[serde(rename_all = "camelCase")]
143pub struct WorkflowsSaveParams {
144    pub run_id: WorkflowRunId,
145    pub name: String,
146    pub scope: WorkflowsSaveScope,
147    #[serde(default)]
148    pub overwrite: bool,
149}
150
151#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
152#[serde(rename_all = "camelCase")]
153pub struct WorkflowsSaveResult {
154    pub script: WorkflowScript,
155}
156
157#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
158#[serde(rename_all = "camelCase")]
159pub struct WorkflowsScriptsListParams {
160    #[serde(default, skip_serializing_if = "Option::is_none")]
161    pub workspace: Option<String>,
162    #[serde(default)]
163    pub include_user: bool,
164    #[serde(default)]
165    pub include_builtin: bool,
166}
167
168#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
169#[serde(rename_all = "camelCase")]
170pub struct WorkflowsScriptsListResult {
171    pub scripts: Vec<WorkflowScript>,
172}
173
174#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
175#[serde(rename_all = "camelCase")]
176pub struct WorkflowsScriptsReadParams {
177    #[serde(default, skip_serializing_if = "Option::is_none")]
178    pub script_id: Option<WorkflowScriptId>,
179    #[serde(default, skip_serializing_if = "Option::is_none")]
180    pub name: Option<String>,
181    #[serde(default, skip_serializing_if = "Option::is_none")]
182    pub source: Option<WorkflowScriptSourceKind>,
183    #[serde(default)]
184    pub include_body: bool,
185}
186
187#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
188#[serde(rename_all = "camelCase")]
189pub struct WorkflowsScriptsReadResult {
190    pub script: WorkflowScript,
191}
192
193#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
194#[serde(rename_all = "camelCase")]
195pub struct WorkflowsScriptsDeleteParams {
196    pub script_id: WorkflowScriptId,
197    #[serde(default)]
198    pub delete_file: bool,
199}
200
201#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
202#[serde(rename_all = "camelCase")]
203pub struct WorkflowsScriptsDeleteResult {
204    pub script_id: WorkflowScriptId,
205    pub deleted: bool,
206}
207
208#[cfg(test)]
209mod tests {
210    use super::*;
211
212    #[test]
213    fn workflow_protocol_params_use_camel_case_fields() {
214        let plan: WorkflowsPlanParams = serde_json::from_value(serde_json::json!({
215            "threadId": "thread-1",
216            "turnId": "turn-1",
217            "prompt": "use a workflow to audit auth",
218            "workspace": "/tmp/repo",
219            "arguments": { "scope": "auth" }
220        }))
221        .unwrap();
222        assert_eq!(plan.thread_id.as_deref(), Some("thread-1"));
223        assert_eq!(plan.turn_id.as_deref(), Some("turn-1"));
224
225        let approve: WorkflowsApproveParams = serde_json::from_value(serde_json::json!({
226            "runId": "run-1",
227            "decision": "alwaysForScriptAndWorkspace",
228            "reason": "trusted generated workflow"
229        }))
230        .unwrap();
231        assert_eq!(
232            approve.decision,
233            WorkflowApprovalDecision::AlwaysForScriptAndWorkspace
234        );
235
236        let restart: WorkflowsRestartAgentParams = serde_json::from_value(serde_json::json!({
237            "runId": "run-1",
238            "agentId": "agent-1"
239        }))
240        .unwrap();
241        assert_eq!(restart.agent_id, "agent-1");
242
243        let read: WorkflowsScriptsReadParams = serde_json::from_value(serde_json::json!({
244            "name": "deep-research",
245            "source": "builtIn",
246            "includeBody": true
247        }))
248        .unwrap();
249        assert_eq!(read.source, Some(WorkflowScriptSourceKind::BuiltIn));
250        assert!(read.include_body);
251
252        let delete = serde_json::to_value(WorkflowsScriptsDeleteResult {
253            script_id: "script-1".to_string(),
254            deleted: true,
255        })
256        .unwrap();
257        assert_eq!(delete["scriptId"], "script-1");
258    }
259}