Skip to main content

nexus_sdk/move_bindings/extensions/
workflow.rs

1//! SDK conveniences for generated workflow and verifier bindings.
2//!
3//! Workflow code turns generated events and execution objects into SDK lookup keys, display
4//! values, and timeout decisions. These helpers keep that translation local to generated types so
5//! BCS layout and runtime behavior remain coupled to the Move ABI.
6
7use crate::{
8    move_bindings::{
9        interface::graph::{PostFailureAction, RuntimeVertex},
10        workflow::{
11            execution::{DAGExecution, DAGExecutionInnerV1, DAGWalk},
12            execution_events::RequestWalkExecutionEvent,
13            execution_failure::WorkflowFailureClass,
14        },
15    },
16    sui,
17    types::{RequestWalkContext, SkillRevisionLookupKey},
18};
19
20impl RequestWalkExecutionEvent {
21    pub fn skill_revision_key(&self) -> Option<SkillRevisionLookupKey> {
22        Some(SkillRevisionLookupKey {
23            agent_id: self.agent_id.into(),
24            skill_id: self.skill_id,
25            interface_revision: self.interface_version,
26        })
27    }
28
29    pub fn to_context(&self) -> anyhow::Result<Option<RequestWalkContext>> {
30        Ok(Some(RequestWalkContext {
31            agent_id: self.agent_id.into(),
32            skill_id: self.skill_id,
33            interface_revision: self.interface_version,
34            task_id: self.task_id.bytes,
35            occurrence_id: self.occurrence_id,
36        }))
37    }
38}
39
40impl PostFailureAction {
41    pub const fn as_str(&self) -> &'static str {
42        match self {
43            Self::Terminate => "terminate",
44            Self::TransientContinue => "continue",
45        }
46    }
47}
48
49impl std::fmt::Display for PostFailureAction {
50    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51        f.write_str(self.as_str())
52    }
53}
54
55impl std::fmt::Display for WorkflowFailureClass {
56    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57        let value = match self {
58            Self::Retryable => "retryable",
59            Self::TerminalToolFailure => "terminal_tool_failure",
60            Self::TerminalSubmissionFailure => "terminal_submission_failure",
61        };
62
63        f.write_str(value)
64    }
65}
66
67impl DAGExecution {
68    /// Returns the stable execution object ID.
69    pub fn object_id(&self) -> sui::types::Address {
70        self.id.id.bytes
71    }
72}
73
74impl DAGExecutionInnerV1 {
75    pub fn dag_id(&self) -> sui::types::Address {
76        self.dag.bytes
77    }
78
79    pub fn agent_id_address(&self) -> sui::types::Address {
80        self.agent_id.bytes
81    }
82
83    pub fn task_id_address(&self) -> sui::types::Address {
84        self.task_id.bytes
85    }
86
87    pub fn occurrence_id_value(&self) -> u64 {
88        self.occurrence_id
89    }
90
91    pub fn skill_revision_key(&self) -> Option<SkillRevisionLookupKey> {
92        Some(SkillRevisionLookupKey {
93            agent_id: self.agent_id_address(),
94            skill_id: self.skill_id,
95            interface_revision: self.interface_version,
96        })
97    }
98
99    pub fn to_context(&self) -> anyhow::Result<Option<RequestWalkContext>> {
100        Ok(Some(RequestWalkContext {
101            agent_id: self.agent_id_address(),
102            skill_id: self.skill_id,
103            interface_revision: self.interface_version,
104            task_id: self.task_id_address(),
105            occurrence_id: self.occurrence_id_value(),
106        }))
107    }
108}
109
110impl DAGWalk {
111    pub fn timeout_expired_vertex(&self, clock_ms: u64) -> Option<&RuntimeVertex> {
112        match self {
113            Self::Active {
114                next_vertex,
115                timeout_ms,
116                created_at,
117                ..
118            }
119            | Self::PendingSettlement {
120                next_vertex,
121                timeout_ms,
122                created_at,
123                ..
124            } if clock_ms >= created_at.saturating_add(timeout_ms.saturating_mul(2)) => {
125                Some(next_vertex)
126            }
127            _ => None,
128        }
129    }
130
131    pub fn abortable_timeout_expired_vertex(&self, clock_ms: u64) -> Option<&RuntimeVertex> {
132        match self {
133            Self::Active {
134                next_vertex,
135                timeout_ms,
136                created_at,
137                ..
138            } if clock_ms >= created_at.saturating_add(timeout_ms.saturating_mul(2)) => {
139                Some(next_vertex)
140            }
141            _ => None,
142        }
143    }
144
145    pub fn expired_active_vertex(&self, clock_ms: u64) -> Option<&RuntimeVertex> {
146        self.abortable_timeout_expired_vertex(clock_ms)
147    }
148}