Skip to main content

rootcx_types/
lib.rs

1use serde::{Deserialize, Serialize};
2use serde_json::Value as JsonValue;
3
4#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct OsStatus {
6    pub runtime: RuntimeStatus,
7    pub postgres: PostgresStatus,
8    pub forge: ForgeStatus,
9}
10
11impl OsStatus {
12    pub fn offline() -> Self {
13        Self {
14            runtime: RuntimeStatus { version: String::new(), state: ServiceState::Offline },
15            postgres: PostgresStatus { state: ServiceState::Offline, port: None, data_dir: None },
16            forge: ForgeStatus { state: ServiceState::Offline, port: None },
17        }
18    }
19}
20
21#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct ForgeStatus {
23    pub state: ServiceState,
24    pub port: Option<u16>,
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct RuntimeStatus {
29    pub version: String,
30    pub state: ServiceState,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct PostgresStatus {
35    pub state: ServiceState,
36    pub port: Option<u16>,
37    pub data_dir: Option<String>,
38}
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
41#[serde(rename_all = "lowercase")]
42pub enum ServiceState {
43    Online,
44    Offline,
45    Starting,
46    Stopping,
47    Error,
48}
49
50#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
51#[serde(rename_all = "lowercase")]
52pub enum AppType {
53    #[default]
54    App,
55    Integration,
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize)]
59#[serde(rename_all = "camelCase")]
60pub struct ActionDefinition {
61    pub id: String,
62    pub name: String,
63    #[serde(default)]
64    pub description: String,
65    #[serde(default)]
66    pub input_schema: Option<JsonValue>,
67    #[serde(default)]
68    pub output_schema: Option<JsonValue>,
69}
70
71#[derive(Debug, Clone, Serialize, Deserialize)]
72#[serde(rename_all = "camelCase")]
73pub struct AppManifest {
74    pub app_id: String,
75    pub name: String,
76    #[serde(default = "default_version")]
77    pub version: String,
78    #[serde(default)]
79    pub description: String,
80    #[serde(default, rename = "type")]
81    pub app_type: AppType,
82    #[serde(default)]
83    pub permissions: Option<PermissionsContract>,
84    #[serde(default)]
85    pub data_contract: Vec<EntityContract>,
86    #[serde(default, skip_serializing_if = "Vec::is_empty")]
87    pub actions: Vec<ActionDefinition>,
88    #[serde(default, skip_serializing_if = "Option::is_none")]
89    pub config_schema: Option<JsonValue>,
90    #[serde(default, skip_serializing_if = "Option::is_none")]
91    pub user_auth: Option<JsonValue>,
92    #[serde(default, skip_serializing_if = "Vec::is_empty")]
93    pub webhooks: Vec<String>,
94    /// Free-form usage instructions surfaced to AI via list_integrations tool
95    #[serde(default, skip_serializing_if = "Option::is_none")]
96    pub instructions: Option<String>,
97}
98
99fn default_version() -> String {
100    "0.0.1".to_string()
101}
102
103#[derive(Debug, Clone, Serialize, Deserialize)]
104#[serde(rename_all = "camelCase")]
105pub struct EntityContract {
106    pub entity_name: String,
107    pub fields: Vec<FieldContract>,
108    #[serde(default, skip_serializing_if = "Option::is_none")]
109    pub identity_kind: Option<String>,
110    #[serde(default, skip_serializing_if = "Option::is_none")]
111    pub identity_key: Option<String>,
112}
113
114#[derive(Debug, Clone, Serialize, Deserialize)]
115#[serde(rename_all = "camelCase")]
116pub struct FieldContract {
117    pub name: String,
118    #[serde(rename = "type")]
119    pub field_type: String,
120    #[serde(default)]
121    pub required: bool,
122    #[serde(default)]
123    pub default_value: Option<JsonValue>,
124    #[serde(default)]
125    pub enum_values: Option<Vec<String>>,
126    #[serde(default)]
127    pub references: Option<FieldReference>,
128    #[serde(default)]
129    pub is_primary_key: Option<bool>,
130}
131
132#[derive(Debug, Clone, Serialize, Deserialize)]
133pub struct FieldReference {
134    pub entity: String,
135    pub field: String,
136}
137
138#[derive(Debug, Clone, Serialize, Deserialize)]
139#[serde(rename_all = "camelCase")]
140pub struct InstalledApp {
141    pub id: String,
142    pub name: String,
143    pub version: String,
144    pub status: String,
145    pub entities: Vec<String>,
146    #[serde(default)]
147    pub has_frontend: bool,
148}
149
150#[derive(Debug, Clone, Serialize, Deserialize)]
151#[serde(rename_all = "camelCase")]
152pub struct PermissionsContract {
153    #[serde(default)]
154    pub permissions: Vec<PermissionDeclaration>,
155}
156
157#[derive(Debug, Clone, Serialize, Deserialize)]
158pub struct PermissionDeclaration {
159    pub key: String,
160    #[serde(default)]
161    pub description: String,
162}
163
164#[derive(Debug, Clone, Serialize, Deserialize)]
165pub struct SchemaChange {
166    pub entity: String,
167    pub change_type: String,
168    pub column: String,
169    pub detail: Option<String>,
170    #[serde(default)]
171    pub safe: bool,
172}
173
174#[derive(Debug, Clone, Serialize, Deserialize)]
175pub struct SchemaVerification {
176    pub compliant: bool,
177    pub changes: Vec<SchemaChange>,
178}
179
180pub const DEFAULT_MODEL: &str = "claude-sonnet-4-6";
181
182#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
183#[serde(rename_all = "lowercase")]
184pub enum ProviderType {
185    Anthropic,
186    OpenAI,
187    Bedrock,
188}
189
190#[derive(Debug, Clone, Serialize, Deserialize)]
191pub struct AiConfig {
192    pub provider: ProviderType,
193    pub model: String,
194    #[serde(default, skip_serializing_if = "Option::is_none")]
195    pub region: Option<String>,
196}
197
198impl Default for AiConfig {
199    fn default() -> Self {
200        Self { provider: ProviderType::Anthropic, model: DEFAULT_MODEL.into(), region: None }
201    }
202}
203
204impl AiConfig {
205    pub fn forge_model_string(&self) -> String {
206        match self.provider {
207            ProviderType::Bedrock => format!("amazon-bedrock/anthropic.{}", self.model),
208            ProviderType::Anthropic => format!("anthropic/{}", self.model),
209            ProviderType::OpenAI => format!("openai/{}", self.model),
210        }
211    }
212}
213
214#[derive(Debug, Clone, Serialize, Deserialize)]
215#[serde(rename_all = "camelCase")]
216pub struct AgentDefinition {
217    pub name: String,
218    #[serde(default)]
219    pub description: Option<String>,
220    #[serde(default)]
221    pub system_prompt: Option<String>,
222    #[serde(default)]
223    pub memory: Option<AgentMemory>,
224    #[serde(default)]
225    pub limits: Option<AgentLimits>,
226    #[serde(default)]
227    pub supervision: Option<SupervisionConfig>,
228}
229
230#[derive(Debug, Clone, Serialize, Deserialize)]
231pub struct AgentMemory {
232    pub enabled: bool,
233}
234
235#[derive(Debug, Clone, Serialize, Deserialize)]
236#[serde(rename_all = "camelCase")]
237pub struct AgentLimits {
238    #[serde(default)]
239    pub max_turns: Option<u32>,
240    #[serde(default)]
241    pub max_context_tokens: Option<u64>,
242    #[serde(default)]
243    pub keep_recent_messages: Option<u32>,
244}
245
246#[derive(Debug, Clone, Serialize, Deserialize)]
247#[serde(rename_all = "camelCase")]
248pub struct SupervisionConfig {
249    pub mode: SupervisionMode,
250    #[serde(default)]
251    pub policies: Vec<SupervisionPolicy>,
252}
253
254#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
255#[serde(rename_all = "lowercase")]
256pub enum SupervisionMode {
257    Autonomous,
258    Supervised,
259    Strict,
260}
261
262#[derive(Debug, Clone, Serialize, Deserialize)]
263#[serde(rename_all = "camelCase")]
264pub struct SupervisionPolicy {
265    pub action: String,
266    #[serde(default)]
267    pub entity: Option<String>,
268    #[serde(default)]
269    pub requires: Option<String>,
270    #[serde(default)]
271    pub rate_limit: Option<RateLimit>,
272}
273
274#[derive(Debug, Clone, Serialize, Deserialize)]
275pub struct RateLimit {
276    pub max: u32,
277    pub window: String,
278}
279
280#[derive(Debug, Clone, Serialize, Deserialize)]
281#[serde(rename_all = "camelCase")]
282pub struct McpServerConfig {
283    pub name: String,
284    pub transport: McpTransport,
285}
286
287#[derive(Debug, Clone, Serialize, Deserialize)]
288#[serde(tag = "type", rename_all = "lowercase")]
289pub enum McpTransport {
290    Stdio { command: String, #[serde(default)] args: Vec<String> },
291    Http { url: String, #[serde(default)] headers: std::collections::HashMap<String, String> },
292    #[deprecated = "use Http"]
293    Sse { url: String, #[serde(default)] headers: std::collections::HashMap<String, String> },
294}
295
296#[derive(Debug, Clone, Serialize, Deserialize)]
297#[serde(rename_all = "camelCase")]
298pub struct ToolDescriptor {
299    pub name: String,
300    pub description: String,
301    pub input_schema: serde_json::Value,
302}
303
304#[derive(Debug, Clone, Serialize, Deserialize)]
305#[serde(rename_all = "lowercase")]
306pub enum Role {
307    User,
308    Assistant,
309}
310
311#[derive(Debug, Clone, Serialize, Deserialize)]
312#[serde(tag = "type", rename_all = "snake_case")]
313pub enum ContentBlock {
314    Text { text: String },
315    ToolUse { id: String, name: String, input: serde_json::Value },
316    ToolResult { tool_use_id: String, content: String, #[serde(default)] is_error: bool },
317}
318
319#[derive(Debug, Clone, Serialize, Deserialize)]
320pub struct ChatMessage {
321    pub role: Role,
322    pub content: Vec<ContentBlock>,
323}
324
325#[derive(Debug, Clone, Serialize, Deserialize)]
326pub struct ToolDef {
327    pub name: String,
328    pub description: String,
329    pub input_schema: serde_json::Value,
330}
331
332impl ProviderType {
333    pub fn secret_key_name(&self) -> &'static str {
334        match self {
335            Self::Anthropic => "ANTHROPIC_API_KEY",
336            Self::OpenAI => "OPENAI_API_KEY",
337            Self::Bedrock => "AWS_BEARER_TOKEN_BEDROCK",
338        }
339    }
340}