1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize};
4
5use runx_contracts::{JsonObject, JsonValue};
6
7#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
8#[serde(deny_unknown_fields)]
9pub struct RawGraphIr {
10 pub document: JsonObject,
11}
12
13#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
14#[serde(rename_all = "camelCase", deny_unknown_fields)]
15pub struct GraphContextEdge {
16 pub input: String,
17 pub from_step: String,
18 pub output: String,
19}
20
21#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
22#[serde(rename_all = "camelCase", deny_unknown_fields)]
23pub struct GraphRetryPolicy {
24 pub max_attempts: u64,
25 #[serde(skip_serializing_if = "Option::is_none")]
26 pub backoff_ms: Option<u64>,
27}
28
29#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
30#[serde(rename_all = "snake_case")]
31pub enum FanoutSyncStrategy {
32 All,
33 Any,
34 Quorum,
35}
36
37#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
38#[serde(rename_all = "snake_case")]
39pub enum FanoutBranchFailurePolicy {
40 Halt,
41 Continue,
42}
43
44#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
45#[serde(rename_all = "snake_case")]
46pub enum FanoutThresholdAction {
47 Pause,
48 Escalate,
49}
50
51#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
52#[serde(rename_all = "camelCase", deny_unknown_fields)]
53pub struct FanoutThresholdGate {
54 pub step: String,
55 pub field: String,
56 pub above: f64,
57 pub action: FanoutThresholdAction,
58}
59
60#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
61#[serde(rename_all = "snake_case")]
62pub enum FanoutConflictAction {
63 Pause,
64 Escalate,
65}
66
67#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
68#[serde(rename_all = "camelCase", deny_unknown_fields)]
69pub struct FanoutConflictGate {
70 pub field: String,
71 pub steps: Vec<String>,
72 pub action: FanoutConflictAction,
73}
74
75#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
76#[serde(rename_all = "camelCase", deny_unknown_fields)]
77pub struct FanoutGroupPolicy {
78 pub group_id: String,
79 pub strategy: FanoutSyncStrategy,
80 #[serde(skip_serializing_if = "Option::is_none")]
81 pub min_success: Option<u64>,
82 pub on_branch_failure: FanoutBranchFailurePolicy,
83 pub threshold_gates: Vec<FanoutThresholdGate>,
84 pub conflict_gates: Vec<FanoutConflictGate>,
85}
86
87#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
88#[serde(rename_all = "camelCase", deny_unknown_fields)]
89pub struct GraphGuard {
90 pub step: String,
91 pub field: String,
92 #[serde(skip_serializing_if = "Option::is_none")]
93 pub equals: Option<JsonValue>,
94 #[serde(skip_serializing_if = "Option::is_none")]
95 pub not_equals: Option<JsonValue>,
96}
97
98#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
99#[serde(deny_unknown_fields)]
100pub struct GraphPolicy {
101 pub guards: Vec<GraphGuard>,
102}
103
104#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
109#[serde(rename_all = "camelCase", deny_unknown_fields)]
110pub struct GraphWhen {
111 pub field: String,
112 #[serde(skip_serializing_if = "Option::is_none")]
113 pub equals: Option<JsonValue>,
114 #[serde(skip_serializing_if = "Option::is_none")]
115 pub not_equals: Option<JsonValue>,
116}
117
118#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
123#[serde(rename_all = "snake_case")]
124pub enum MintScopeSource {
125 StaticScopes,
129 RequestedScope,
133}
134
135#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
142#[serde(rename_all = "camelCase", deny_unknown_fields)]
143pub struct MintAuthorityDirective {
144 pub source: MintScopeSource,
145}
146
147#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
148#[serde(rename_all = "camelCase", deny_unknown_fields)]
149pub struct GraphStep {
150 pub id: String,
151 #[serde(skip_serializing_if = "Option::is_none")]
152 pub label: Option<String>,
153 #[serde(skip_serializing_if = "Option::is_none")]
154 pub skill: Option<String>,
155 #[serde(skip_serializing_if = "Option::is_none")]
156 pub tool: Option<String>,
157 #[serde(skip_serializing_if = "Option::is_none")]
158 pub run: Option<JsonObject>,
159 #[serde(skip_serializing_if = "Option::is_none")]
160 pub instructions: Option<String>,
161 #[serde(skip_serializing_if = "Option::is_none")]
162 pub artifacts: Option<JsonObject>,
163 #[serde(skip_serializing_if = "Option::is_none")]
164 pub runner: Option<String>,
165 pub inputs: JsonObject,
166 pub context: BTreeMap<String, String>,
167 pub context_edges: Vec<GraphContextEdge>,
168 #[serde(default, skip_serializing_if = "Vec::is_empty")]
169 pub context_skills: Vec<String>,
170 pub scopes: Vec<String>,
171 #[serde(skip_serializing_if = "Option::is_none")]
172 pub allowed_tools: Option<Vec<String>>,
173 #[serde(skip_serializing_if = "Option::is_none")]
174 pub retry: Option<GraphRetryPolicy>,
175 #[serde(skip_serializing_if = "Option::is_none")]
176 pub policy: Option<JsonObject>,
177 #[serde(skip_serializing_if = "Option::is_none")]
178 pub fanout_group: Option<String>,
179 #[serde(skip_serializing_if = "Option::is_none")]
180 pub when: Option<GraphWhen>,
181 pub mutating: bool,
182 #[serde(skip_serializing_if = "Option::is_none")]
183 pub idempotency_key: Option<String>,
184 #[serde(skip_serializing_if = "Option::is_none")]
189 pub mint_authority: Option<MintAuthorityDirective>,
190 #[serde(skip_serializing_if = "Option::is_none")]
195 pub requested_scope_from: Option<String>,
196}
197
198#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
199#[serde(rename_all = "camelCase", deny_unknown_fields)]
200pub struct ExecutionGraph {
201 pub name: String,
202 #[serde(skip_serializing_if = "Option::is_none")]
203 pub owner: Option<String>,
204 #[serde(skip_serializing_if = "Option::is_none")]
209 pub charter_from: Option<String>,
210 pub steps: Vec<GraphStep>,
211 pub fanout_groups: BTreeMap<String, FanoutGroupPolicy>,
212 #[serde(skip_serializing_if = "Option::is_none")]
213 pub policy: Option<GraphPolicy>,
214 pub raw: RawGraphIr,
215}