roder-protocol 0.1.2

Agentic software development tools and SDKs for Roder.
Documentation
use roder_api::dynamic_workflows::{
    WorkflowAgentRun, WorkflowAgentRunId, WorkflowApproval, WorkflowApprovalDecision, WorkflowRun,
    WorkflowRunId, WorkflowRunSummary, WorkflowScript, WorkflowScriptId, WorkflowScriptSourceKind,
};
use roder_api::events::{ThreadId, TurnId};
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct WorkflowsPlanParams {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub thread_id: Option<ThreadId>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub turn_id: Option<TurnId>,
    pub prompt: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub workspace: Option<String>,
    #[serde(default)]
    pub arguments: serde_json::Value,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub script: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct WorkflowsPlanResult {
    pub run: WorkflowRun,
    pub approval_required: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct WorkflowsApproveParams {
    pub run_id: WorkflowRunId,
    pub decision: WorkflowApprovalDecision,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub reason: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct WorkflowsApproveResult {
    pub run: WorkflowRun,
    pub approval: WorkflowApproval,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct WorkflowsListParams {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub thread_id: Option<ThreadId>,
    #[serde(default)]
    pub include_terminal: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct WorkflowsListResult {
    pub runs: Vec<WorkflowRunSummary>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct WorkflowsGetParams {
    pub run_id: WorkflowRunId,
    #[serde(default)]
    pub include_script_body: bool,
    #[serde(default)]
    pub include_agents: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct WorkflowsGetResult {
    pub run: WorkflowRun,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct WorkflowsPauseParams {
    pub run_id: WorkflowRunId,
    #[serde(default)]
    pub cancel_running_agents: bool,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub reason: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct WorkflowsPauseResult {
    pub run: WorkflowRun,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct WorkflowsResumeParams {
    pub run_id: WorkflowRunId,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct WorkflowsResumeResult {
    pub run: WorkflowRun,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct WorkflowsStopParams {
    pub run_id: WorkflowRunId,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub reason: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct WorkflowsStopResult {
    pub run: WorkflowRun,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct WorkflowsRestartAgentParams {
    pub run_id: WorkflowRunId,
    pub agent_id: WorkflowAgentRunId,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct WorkflowsRestartAgentResult {
    pub run: WorkflowRun,
    pub agent: WorkflowAgentRun,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum WorkflowsSaveScope {
    User,
    Workspace,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct WorkflowsSaveParams {
    pub run_id: WorkflowRunId,
    pub name: String,
    pub scope: WorkflowsSaveScope,
    #[serde(default)]
    pub overwrite: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct WorkflowsSaveResult {
    pub script: WorkflowScript,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct WorkflowsScriptsListParams {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub workspace: Option<String>,
    #[serde(default)]
    pub include_user: bool,
    #[serde(default)]
    pub include_builtin: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct WorkflowsScriptsListResult {
    pub scripts: Vec<WorkflowScript>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct WorkflowsScriptsReadParams {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub script_id: Option<WorkflowScriptId>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub name: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub source: Option<WorkflowScriptSourceKind>,
    #[serde(default)]
    pub include_body: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct WorkflowsScriptsReadResult {
    pub script: WorkflowScript,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct WorkflowsScriptsDeleteParams {
    pub script_id: WorkflowScriptId,
    #[serde(default)]
    pub delete_file: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct WorkflowsScriptsDeleteResult {
    pub script_id: WorkflowScriptId,
    pub deleted: bool,
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn workflow_protocol_params_use_camel_case_fields() {
        let plan: WorkflowsPlanParams = serde_json::from_value(serde_json::json!({
            "threadId": "thread-1",
            "turnId": "turn-1",
            "prompt": "use a workflow to audit auth",
            "workspace": "/tmp/repo",
            "arguments": { "scope": "auth" }
        }))
        .unwrap();
        assert_eq!(plan.thread_id.as_deref(), Some("thread-1"));
        assert_eq!(plan.turn_id.as_deref(), Some("turn-1"));

        let approve: WorkflowsApproveParams = serde_json::from_value(serde_json::json!({
            "runId": "run-1",
            "decision": "alwaysForScriptAndWorkspace",
            "reason": "trusted generated workflow"
        }))
        .unwrap();
        assert_eq!(
            approve.decision,
            WorkflowApprovalDecision::AlwaysForScriptAndWorkspace
        );

        let restart: WorkflowsRestartAgentParams = serde_json::from_value(serde_json::json!({
            "runId": "run-1",
            "agentId": "agent-1"
        }))
        .unwrap();
        assert_eq!(restart.agent_id, "agent-1");

        let read: WorkflowsScriptsReadParams = serde_json::from_value(serde_json::json!({
            "name": "deep-research",
            "source": "builtIn",
            "includeBody": true
        }))
        .unwrap();
        assert_eq!(read.source, Some(WorkflowScriptSourceKind::BuiltIn));
        assert!(read.include_body);

        let delete = serde_json::to_value(WorkflowsScriptsDeleteResult {
            script_id: "script-1".to_string(),
            deleted: true,
        })
        .unwrap();
        assert_eq!(delete["scriptId"], "script-1");
    }
}